diff --git a/include/generalJsonParsing.hpp b/include/generalJsonParsing.hpp index 171d010..6a811bc 100644 --- a/include/generalJsonParsing.hpp +++ b/include/generalJsonParsing.hpp @@ -46,7 +46,7 @@ static bool saveJsonFromPath(std::string path, nlohmann::json& json) return true; } -// 读取有序 JSON 文件 +// 有序读取 JSON 文件 static nlohmann::ordered_json readOrderedJsonFromPath(const std::string& path) { nlohmann::ordered_json jsonObj; @@ -66,7 +66,7 @@ static nlohmann::ordered_json readOrderedJsonFromPath(const std::string& path) return jsonObj; } -// 保存有序 JSON 文件 +// 有序保存 JSON 文件 static bool saveOrderedJsonFromPath(const std::string& path, const nlohmann::ordered_json& json) { std::ofstream saveFile(path); @@ -85,7 +85,7 @@ static bool saveOrderedJsonFromPath(const std::string& path, const nlohmann::ord return true; } - +//打印json内容 static void printNlohmannJson(nlohmann::json input, int depth = 0) { std::string indent_str(depth, ' '); diff --git a/include/global.h b/include/global.h index 2056ab1..c54aee0 100644 --- a/include/global.h +++ b/include/global.h @@ -11,20 +11,21 @@ #include #pragma execution_character_set("utf-8") -const std::string softwareVersion = "20260303-2.4.1"; -const std::string euiJsonVersion = "1.0.6"; -#define GigaByte 1073741824 +const std::string softwareVersion = "20260416-2.5.1";//版本号 +const std::string euiJsonVersion = "1.0.6";//json标准版本 +#define GigaByte 1073741824//1GB字节大小 #define globalDatabasePth "./sqlDatabase/eui_db.db" //全局数据库路径 #define savePathToDataBase true //是否将存储路径存入到数据库中 #define saveImgEnable true//是否存储图片 #define switchToTongYongUi 1 //是否切换到通用UI界面 #define skipLogin 1 //是否跳过登录界面 -#define imgSaveTreadMaxCount 2 //最大存图线程数 -#define imgSaveThreadMaxCacheGigaByte 5 //存图线程最大缓存数量 单位GB #define jsonRpcEnable false //是否启用JSON RPC服务器 #define minimumWindowSize 1280, 720//最小窗口大小 #define histogramLabelTiltFontSize 8 //柱状图标签字体大小 #define windowMoveFreshRate 60//窗口移动每秒的刷新率 +#define pathDataBaseSaveDate 90//存储存图路径的数据库其数据保留的日期 +#define detDataBaseSaveDate 730//存储检测数据的数据库其数据保留的日期 + #define sColor_Blue "51, 56, 69" #define sColor_BlueWithWhite "64, 68, 81" #define sColor_BlueWithBlack "46, 50, 52" @@ -42,6 +43,24 @@ const std::string euiJsonVersion = "1.0.6"; #define sColor_Black "22, 22, 22" #define sColor_BlackWithWhite "45, 45, 45" +#define REGISTER_META_TYPE_LIST \ + qRegisterMetaType("matPtr"); \ + qRegisterMetaType("stdString"); \ + qRegisterMetaType("historyPack"); \ + qRegisterMetaType("QtGlobalColor"); \ + qRegisterMetaType("showImgPack"); \ + qRegisterMetaType("initStatus"); \ + qRegisterMetaType("mat"); \ + qRegisterMetaType*>("vecmatptr"); \ + qRegisterMetaType>("vectorstring"); \ + qRegisterMetaType>>("constvpqi"); \ + qRegisterMetaType("PlcValueViewerStruct"); \ + qRegisterMetaType>>("vecQStringQVarint"); \ + qRegisterMetaType>("vecQString");\ + qRegisterMetaType("runCommandWidgetType");\ + qRegisterMetaType>>("shared_ptrAtomicInt");\ + qRegisterMetaType>>("shared_ptrAtomicBool");\ + qRegisterMetaType>*>("vecQStringQVarintPtr"); struct viewerStruct { @@ -49,7 +68,10 @@ struct viewerStruct { int xPos, yPos,width,height; std::vectorviewers; }; - +enum runCommandWidgetType { + PlcSim, + Direct +}; enum backendError { dllNotFound, @@ -180,6 +202,8 @@ Q_DECLARE_METATYPE(cv::Mat) Q_DECLARE_METATYPE(std::string) Q_DECLARE_METATYPE(historyPack) Q_DECLARE_METATYPE(Qt::GlobalColor) +Q_DECLARE_METATYPE(std::shared_ptr>) +Q_DECLARE_METATYPE(std::shared_ptr>) diff --git a/include/interface.h b/include/interface.h index 7c1d5bf..54d5d02 100644 --- a/include/interface.h +++ b/include/interface.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include enum initStatus { NotInit, @@ -29,7 +30,8 @@ struct PlcValueViewerStruct { QVariant value = -999; }; -class bSignalHub :public QObject{ +//后端信号集成 +class bSignalHub :public QObject { Q_OBJECT public: explicit bSignalHub(QObject* parent = nullptr) : QObject(parent) {} @@ -47,36 +49,73 @@ signals: // 图像显示 void displayWidget_sendImg(showImgPack pack); //数据到界面 + //饼图 void pieWidgetModifyData(int modifyOkData, int modiftNgData); - void histogramWidgetAddData(int modifyOkData, int modiftNgData,std::vector); + //柱状图 + void histogramWidgetAddData(int modifyOkData, int modiftNgData, std::vector); + //设置PLC连接失败(闪红灯) void setPlcConnectionError(); + //设置PLC连接成功(绿灯常亮) void setPlcConnectionOk(); + //设置相机连接失败(闪红灯) void setCamConnectionError(); + //设置相机连接成功(绿灯常亮) void setCamConnectionOk(); + //数据到折线图 void waveFormAddData(QString windowName, double value); //排障 - void runCommand_addBtn(const QString& btnName, const QString& tip, const QString& uuid); - void runCommand_addPlcValueViewer(QString groupName, std::vector> viewerNameAndAddress); - void runCommand_refeshPlcValueViewer(int address, QString value); + //在排障界面添加功能按钮 + void runCommand_addBtn(runCommandWidgetType,const QString& btnName, const QString& tip, const QString& uuid); + //在排障界面添加PLC地址值监控控件 + void runCommand_addPlcValueViewer(const QString& , const std::vector>&); + //刷新排障界面PLC监控的值 + void runCommand_refeshPlcValueViewer(const int& address, const QString& value); + + //算法耗时监测 + void halconManager_addTimeCostViewer(const QString& groupName, const std::vector& viewerNames); + void halconManager_refreshTimeCostViewer(const QString& address, const QString& avgTime, const QString& maxTime, const QString& minTime); + + //模型耗时监测 + void handleAiManager_addTimeCostViewer(const QString& groupName, const std::vector& viewerNames); + void handleAiManager_refreshTimeCostViewer(const QString& address, const QString& avgTime, const QString& maxTime, const QString& minTime); + + //存储任务json void saveMissionJson(); - //性能监测 + //性能监测-将每秒与PLC交互的次数等信息发送到主页面 void sendTriggerManagerLoopData(int loopCounterPerMin, int maxInterval); + //添加命令预设 + void Cli_addPreset(QString btnName, QString tips, QStringList commands); + //在模型界面添加功能按钮 + void modelInit_addBtn(const QString& btnName, const QString& tip, const QString& command); + //在通过界面将模型初始化的结果 + void modelInit_modelInitResult(const bool& initSuccess, const QString& Reason); + //后端任务等待前端初始化任务确认 + void waitForFrontendInitComfirm(const QString& message, const std::vector& options,std::shared_ptr> recivedResult,std::shared_ptr> Result); + //弹窗 + void showMessageBox(const QString& message); + //备份及替换halcon算法文件 + void backupAndReplaceAlgoFile(const std::string& path); + + void reloadAllCameraAsVirtual(); }; + + +//后端对象 class IBackend :public QThread { Q_OBJECT public: - bSignalHub _signalHub; + bSignalHub _signalHub; //后端信号集成 virtual ~IBackend() = default; virtual void init(SQLiteDB* ,nlohmann::ordered_json& json) = 0; - virtual nlohmann::ordered_json getBackendJson() = 0; + virtual nlohmann::ordered_json getBackendJson() = 0;//获取后端配置文件 public slots: - virtual bool excuteCliCommand(const QString& command) = 0; - virtual bool excuteCliCommandPreset(const QString& uuid) = 0; - virtual void triggerAndCaptureImage(std::string cameraName, std::vector* mats) = 0; - virtual void getCameraNames(std::vector* cameraNames) = 0; - virtual void getAllParams(std::vector>* params) = 0; - virtual void editParam(QString group, QString name, QVariant value) = 0; + virtual bool ansyncExcuteCliCommand(const QString& command) = 0;//执行指令 + virtual bool ansyncExcuteCliCommandPreset(const QString& uuid) = 0;//执行预先注册的指令 + virtual void triggerAndCaptureImage(std::string cameraName, std::vector* mats) = 0;//触发并获取返回图片 + virtual void getCameraNames(std::vector* cameraNames) = 0;//获取所有相机的名称 + virtual void getAllParams(std::vector>* params) = 0;//获取所有参数及值 + virtual void editParam(QString group, QString name, QVariant value) = 0;//修改指定参数的值 }; extern "C" { diff --git a/include/opencvPaintAlgo.hpp b/include/opencvPaintAlgo.hpp index 24af312..015f323 100644 --- a/include/opencvPaintAlgo.hpp +++ b/include/opencvPaintAlgo.hpp @@ -1,7 +1,9 @@ #pragma once +#include +#include +#include #include #include - #pragma region 画叉 static void Cross(cv::Mat& image, const cv::Point& center, int length, int thickness, const cv::Scalar& color = cv::Scalar(255, 255, 255)) { // 检查输入图像是否有效 @@ -48,6 +50,16 @@ static void drawArrow(cv::Mat& Paint, const cv::Point& start, const int& length, } } } + +void drawRotatedRect(cv::Mat& img, cv::RotatedRect rotateRect, cv::Scalar color, int thickness, int linType) +{ + std::vector boxPts(4); + rotateRect.points(boxPts.data()); + for (int mm = 0; mm < 4; mm++) { + cv::line(img, boxPts[mm], boxPts[(mm + 1) % 4], color, thickness, linType); + } +} + #pragma region 写中文 static std::wstring utf8ToWstring(const std::string& utf8Str) { int len = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, nullptr, 0); @@ -57,7 +69,7 @@ static std::wstring utf8ToWstring(const std::string& utf8Str) { wstr.pop_back(); // remove null terminator return wstr; } -// 在图像上绘制中文(仅 Windows,使用 GDI) +// 在图像上绘制中文(仅 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)) { if (image.channels() != 3) throw std::runtime_error("Only supports 3-channel images"); @@ -106,7 +118,7 @@ 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: TextRendererZH(const TextRendererZH&) = delete; @@ -117,9 +129,14 @@ public: TextRendererZH(int width, int height, int fontSize = 20, const std::wstring& fontName = L"微软雅黑") : width_(width), height_(height), fontSize_(fontSize), fontName_(fontName) { + if (width_ <= 0) width_ = 1; + if (height_ <= 0) height_ = 1; + if (fontSize_ <= 0) fontSize_ = 20; + screenDC_ = GetDC(nullptr); if (!screenDC_) { - throw std::runtime_error("Failed to get screen DC"); + logMessage("TextRendererZH: 获取屏幕 DC 失败,文本绘制已禁用。"); + return; } createResources(); } @@ -131,12 +148,25 @@ public: void putText(cv::Mat& image, const std::string& text, cv::Point org, cv::Scalar color = cv::Scalar(255, 0, 0)) { QMutexLocker locker(&mutex_); + + if (text.empty()) return; + + if (!screenDC_ || !memDC_ || !hBitmap_ || !pBits_ || !hFont_) { + logMessage("TextRendererZH: 资源未就绪,跳过文本绘制。"); + return; + } - if (image.channels() != 3) - throw std::runtime_error("TextRendererZH only supports 3-channel BGR images."); + if (image.channels() != 3) { + logMessage("TextRendererZH: 仅支持 3 通道 BGR 图像,跳过文本绘制。"); + return; + } if (image.cols != width_ || image.rows != height_) { resize_unsafe(image.cols, image.rows); + if (!memDC_ || !hBitmap_ || !pBits_ || !hFont_) { + logMessage("TextRendererZH: 尺寸调整失败,跳过文本绘制。"); + return; + } } cv::Mat img; @@ -160,8 +190,28 @@ public: resize_unsafe(width, height); } + void setFontSize(int fontSize) { + if (fontSize <= 0) { + logMessage("TextRendererZH: 字体大小必须大于 0,保持当前字体大小。"); + return; + } + + QMutexLocker locker(&mutex_); + if (fontSize_ == fontSize) return; + + fontSize_ = fontSize; + if (!screenDC_) return; + + releaseResources(); + createResources(); + } + private: void resize_unsafe(int width, int height) { + if (width <= 0 || height <= 0) { + logMessage("TextRendererZH: 目标图像尺寸非法,保持当前渲染尺寸。"); + return; + } if (width == width_ && height == height_) return; releaseResources(); @@ -171,143 +221,22 @@ private: createResources(); } -public: - - struct pointPaintJob { - cv::Point center; - cv::Scalar color; - }; - void appendPaintJob(cv::Point center, cv::Scalar color) { - QMutexLocker locker(&mutex_); - pointPaintJobs.push_back(pointPaintJob{ center,color }); - } - - struct contourPaintJob { - std::vector contour; - cv::Scalar color; - int thickness; - }; - void appendPaintJob(std::vector contour, cv::Scalar color, int thickness) { - QMutexLocker locker(&mutex_); - contourPaintJobs.push_back(contourPaintJob{ contour,color,thickness }); - } - - struct linePaintJob { - cv::Point p1; - cv::Point p2; - cv::Scalar color; - int thickness; - }; - void appendPaintJob(cv::Point p1, cv::Point p2, cv::Scalar color, int thickness) { - QMutexLocker locker(&mutex_); - linePaintJobs.push_back(linePaintJob{ p1,p2,color,thickness }); - } - - struct circleAndCorssPaintJob { - cv::Point center; - double radius; - cv::Scalar color; - int thickness; - int type; - }; - //type0 圆 type1 十字 - void appendPaintJob(cv::Point center, double radius,cv::Scalar color, int thickness,int type) { - QMutexLocker locker(&mutex_); - circleAndCorssPaintJobs.push_back(circleAndCorssPaintJob{ center,radius,color,thickness,type }); - } - - struct rectPaintJob { - cv::Rect rect; - cv::Scalar color; - int thickness; - }; - void appendPaintJob(cv::Rect rect, cv::Scalar color,int thickness) { - QMutexLocker locker(&mutex_); - rectPaintJobs.push_back(rectPaintJob{ rect,color,thickness }); - } - - struct textPaintJob { - std::string text; - cv::Point p; - cv::Scalar color; - }; - void appendPaintJob(std::string text, cv::Point p, cv::Scalar color) { - QMutexLocker locker(&mutex_); - textPaintJobs.push_back(textPaintJob{ text,p,color }); - } - - void executePaintJobs(cv::Mat& image,double resizeRate = 1.0) { - QMutexLocker locker(&mutex_); - - if (textPaintJobs.empty() && pointPaintJobs.empty() && circleAndCorssPaintJobs.empty() && rectPaintJobs.empty() && contourPaintJobs.empty() && linePaintJobs.empty()) return; - - if (image.channels() != 3) - throw std::runtime_error("TextRendererZH only supports 3-channel BGR images."); - - if (image.cols != width_ || image.rows != height_) { - resize_unsafe(image.cols, image.rows); - } - - // 绘制点 - for (const auto& job : pointPaintJobs) { - cv::circle(image, job.center, 1, job.color, -1); - } - - // 绘制圆或十字 - for (const auto& job : circleAndCorssPaintJobs) { - if(job.type == 0) - cv::circle(image, job.center, job.radius, job.color, job.thickness); - else - cv::drawMarker(image, job.center, job.color, cv::MARKER_CROSS, job.radius, job.thickness); - } - - // 绘制点集 - for (const auto& job : contourPaintJobs) { - cv::drawContours(image, { job.contour }, 0, job.color, job.thickness); - } - - // 绘制线段 - for (const auto& job : linePaintJobs) { - cv::line(image, job.p1, job.p2, job.color, job.thickness); - } - - // 绘制矩形 - for (const auto& job : rectPaintJobs) { - cv::rectangle(image, job.rect, job.color, job.thickness); - } - - // 绘制文本(需要GDI处理) - if (!textPaintJobs.empty()) { - // 只进行一次颜色转换和数据拷贝 - cv::Mat img; - cv::cvtColor(image, img, cv::COLOR_BGR2BGRA); - memcpy(pBits_, img.data, img.total() * img.elemSize()); - - SetBkMode(memDC_, TRANSPARENT); - - // 批量绘制所有文本 - for (const auto& job : textPaintJobs) { - SetTextColor(memDC_, RGB(job.color[2], job.color[1], job.color[0])); - std::wstring wtext = utf8ToWstring(job.text); - TextOutW(memDC_, job.p.x, job.p.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); - } - - // 清空已执行的任务 - contourPaintJobs.clear(); - linePaintJobs.clear(); - pointPaintJobs.clear(); - circleAndCorssPaintJobs.clear(); - rectPaintJobs.clear(); - textPaintJobs.clear(); - } private: + static void logMessage(const char* message) { + std::cout << message << std::endl; + } + void createResources() { + if (!screenDC_) { + logMessage("TextRendererZH: 屏幕 DC 不可用,无法创建资源。"); + return; + } + memDC_ = CreateCompatibleDC(screenDC_); + if (!memDC_) { + logMessage("TextRendererZH: 创建内存 DC 失败。"); + return; + } memset(&bmi_, 0, sizeof(bmi_)); bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); @@ -318,12 +247,22 @@ private: bmi_.bmiHeader.biCompression = BI_RGB; hBitmap_ = CreateDIBSection(memDC_, &bmi_, DIB_RGB_COLORS, &pBits_, nullptr, 0); + if (!hBitmap_ || !pBits_) { + logMessage("TextRendererZH: 创建位图资源失败。"); + releaseResources(); + return; + } 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()); + if (!hFont_) { + logMessage("TextRendererZH: 创建字体资源失败。"); + releaseResources(); + return; + } SelectObject(memDC_, hFont_); } @@ -365,17 +304,223 @@ private: BITMAPINFO bmi_{}; HFONT hFont_ = nullptr; - - // 任务队列 - std::vector textPaintJobs; - std::vector pointPaintJobs; - std::vector circleAndCorssPaintJobs; - std::vector linePaintJobs; - std::vector contourPaintJobs; - std::vector rectPaintJobs; - - // 线程保护 + + QMutex mutex_; +}; + +//缓存绘画数据在调用executePaintJobs统一绘画 +class cvPainter { +public: + cvPainter(const int& fontSize = 50) { + defaultFontSize = fontSize; + } + cvPainter(const cvPainter&) = delete; + cvPainter& operator=(const cvPainter&) = delete; + // QMutex不可移动,禁用移动操作 + cvPainter(cvPainter&&) = delete; + cvPainter& operator=(cvPainter&&) = delete; + ~cvPainter() = default; + +private: + int defaultFontSize = 50; + static void logMessage(const char* message) { + std::cout << message << std::endl; + } + +public: + + // 清空所有待执行任务 + void clearPaintJobs() { + QMutexLocker locker(&mutex_); + contourPaintJobs.clear(); + linePaintJobs.clear(); + pointPaintJobs.clear(); + circleAndCrossPaintJobs.clear(); + rectPaintJobs.clear(); + textPaintJobs.clear(); + } + + struct pointPaintJob { + cv::Point center; + cv::Scalar color; + }; + void appendPaintJob(cv::Point center, cv::Scalar color) { + QMutexLocker locker(&mutex_); + pointPaintJobs.emplace_back(pointPaintJob{ center, color }); + } + + struct contourPaintJob { + std::vector> contour; + int i; + cv::Scalar color; + int thickness; + int lineType = cv::LINE_8; + cv::Mat hierarchy; // 用cv::Mat存储,避免cv::InputArray产生悬空引用 + int maxLevel = INT_MAX; + cv::Point offset; + }; + void appendPaintJob(std::vector> contour, + int i, + cv::Scalar color, int thickness, + int lineType = cv::LINE_8, + cv::InputArray hierarchy = cv::noArray(), + int maxLevel = INT_MAX, cv::Point offset = cv::Point()) { + QMutexLocker locker(&mutex_); + contourPaintJobs.emplace_back(contourPaintJob{ + std::move(contour), i, color, thickness, lineType, + hierarchy.getMat(), maxLevel, offset }); + } + + struct linePaintJob { + cv::Point p1; + cv::Point p2; + cv::Scalar color; + int thickness; + }; + void appendPaintJob(cv::Point p1, cv::Point p2, cv::Scalar color, int thickness) { + if (thickness <= 0) { + logMessage("cvPainter: 线段粗细必须大于 0,已忽略该绘制任务。"); + return; + } + QMutexLocker locker(&mutex_); + linePaintJobs.emplace_back(linePaintJob{ p1, p2, color, thickness }); + } + + struct circleAndCrossPaintJob { + cv::Point center; + int radius; // int,与cv::circle / cv::drawMarker接口一致 + cv::Scalar color; + int thickness; + int type; // 0=圆, 1=十字 + }; + // type=0:圆 type=1:十字 + void appendPaintJob(cv::Point center, int radius, cv::Scalar color, int thickness, int type) { + if (radius <= 0) { + logMessage("cvPainter: 半径必须大于 0,已忽略该绘制任务。"); + return; + } + if (thickness <= 0) { + logMessage("cvPainter: 圆形或十字的线宽必须大于 0,已忽略该绘制任务。"); + return; + } + if (type != 0 && type != 1) { + logMessage("cvPainter: type 只能为 0(圆)或 1(十字),已忽略该绘制任务。"); + return; + } + QMutexLocker locker(&mutex_); + circleAndCrossPaintJobs.emplace_back(circleAndCrossPaintJob{ center, radius, color, thickness, type }); + } + + struct rectPaintJob { + cv::Rect rect; + cv::Scalar color; + int thickness; + }; + void appendPaintJob(cv::Rect rect, cv::Scalar color, int thickness) { + if (rect.empty()) { + logMessage("cvPainter: 矩形不能为空,已忽略该绘制任务。"); + return; + } + if (thickness == 0) { + logMessage("cvPainter: 矩形线宽不能为 0,已忽略该绘制任务。"); + return; + } + QMutexLocker locker(&mutex_); + rectPaintJobs.emplace_back(rectPaintJob{ rect, color, thickness }); + } + + struct textPaintJob { + std::string text; + cv::Point p; + cv::Scalar color; + int fontSize; + }; + void appendPaintJob(const std::string& text, cv::Point p, cv::Scalar color, int fontSize = -1) { + if (text.empty()) return; + if (fontSize == -1) + fontSize = defaultFontSize; + QMutexLocker locker(&mutex_); + textPaintJobs.emplace_back(textPaintJob{ text, p, color, fontSize }); + } + + void executePaintJobs(cv::Mat& image) { + QMutexLocker locker(&mutex_); + + if (textPaintJobs.empty() && pointPaintJobs.empty() && circleAndCrossPaintJobs.empty() && + rectPaintJobs.empty() && contourPaintJobs.empty() && linePaintJobs.empty()) return; + + if (image.empty()) { + logMessage("cvPainter: 输入图像为空,已保留当前绘制任务。"); + return; + } + if (image.channels() != 3) { + logMessage("cvPainter: 仅支持 3 通道 BGR 图像,已保留当前绘制任务。"); + return; + } + + // 绘制点 + for (const auto& job : pointPaintJobs) { + cv::circle(image, job.center, 1, job.color, -1); + } + + // 绘制圆或十字 + for (const auto& job : circleAndCrossPaintJobs) { + if (job.type == 0) + cv::circle(image, job.center, job.radius, job.color, job.thickness); + else + cv::drawMarker(image, job.center, job.color, cv::MARKER_CROSS, job.radius, job.thickness); + } + + // 绘制轮廓 + for (const auto& job : contourPaintJobs) { + cv::drawContours(image, job.contour, job.i, job.color, job.thickness, + job.lineType, job.hierarchy, job.maxLevel, job.offset); + } + + // 绘制线段 + for (const auto& job : linePaintJobs) { + cv::line(image, job.p1, job.p2, job.color, job.thickness); + } + + // 绘制矩形 + for (const auto& job : rectPaintJobs) { + cv::rectangle(image, job.rect, job.color, job.thickness); + } + + // 使用 TextRendererZH 绘制文本,支持中文 + for (const auto& job : textPaintJobs) { + auto it = textRenderers_.find(job.fontSize); + if (it == textRenderers_.end()) { + auto renderer = std::make_unique(image.cols, image.rows, job.fontSize); + auto inserted = textRenderers_.emplace(job.fontSize, std::move(renderer)); + it = inserted.first; + } + + it->second->putText(image, job.text, job.p, job.color); + } + + // 清空已执行的任务 + contourPaintJobs.clear(); + linePaintJobs.clear(); + pointPaintJobs.clear(); + circleAndCrossPaintJobs.clear(); + rectPaintJobs.clear(); + textPaintJobs.clear(); + } + +private: + std::vector textPaintJobs; + std::vector pointPaintJobs; + std::vector circleAndCrossPaintJobs; + std::vector linePaintJobs; + std::vector contourPaintJobs; + std::vector rectPaintJobs; + + // 按字体大小缓存文本渲染器 + std::unordered_map> textRenderers_; + mutable QMutex mutex_; }; +# #pragma endregion diff --git a/include/paramSpecifications.h b/include/paramSpecifications.h new file mode 100644 index 0000000..938e42d --- /dev/null +++ b/include/paramSpecifications.h @@ -0,0 +1,82 @@ + + +struct valueControlStruct { + double minThreshold; + double maxThreshold; + bool enable; +}; +//生成一个数据的上下限与是否启用 +//类名 值名 单位名 默认下限 默认上限 默认是否启用 数据同步类 +valueControlStruct genValueControlStruct(QString className, + QString valueName, + QString unitStr, + double basicMaxThreshold, + double basicMinThreshold, + bool basicEnable, + paramSyncManager* sync) { + valueControlStruct r; + r.maxThreshold = sync->get(className, valueName + "上限(" + unitStr + ")", basicMaxThreshold).toDouble(); + r.minThreshold = sync->get(className, valueName + "下限(" + unitStr + ")", basicMinThreshold).toDouble(); + r.enable = sync->get(className, valueName + "判定启用", basicEnable).toBool(); + return r; +} + + +std::pair getPerfectOutputValue( + double realValue, + double minValue, + double maxValue, + double offsetValue) +{ + static thread_local std::mt19937 rng(std::random_device{}()); + std::uniform_real_distribution dist(0.0, 0.1); //随机数 + + if (realValue < minValue) {// 低于下限 + if (minValue - realValue <= offsetValue) {// 在补偿范围内 + return { true, minValue + dist(rng) };// 返回补偿后的值 + } + return { false, realValue }; // 不在在补偿范围内 返回真实值 + } + else if (realValue > maxValue) { + if (realValue - maxValue <= offsetValue) {// 在补偿范围内 + return { true, maxValue - dist(rng) };// 返回补偿后的值 + } + return { false, realValue }; // 不在在补偿范围内 返回真实值 + } + + return { false, realValue };// 在范围内,无需补偿,返回真实值 +} +std::string controlAccuracy(float num, int length) { + std::string resultString = std::to_string(num); + resultString = resultString.substr(0, resultString.find(".") + length + 1); + return resultString; +} +//包含判断结果 输出打印任务 补偿 +void valueJudgement(double value, double upThreshold, double downThreshold, double offset, + bool jugementEnable, bool& halconOk, std::string valueDisplayText, std::string valueDanWei, std::string ngText, + cv::Point p, std::vector& defects, cvPainter& _cvPainter) { + cv::Scalar color = cv::Scalar(0, 255, 0); + double newValue = getPerfectOutputValue(value, downThreshold, upThreshold, offset).second; + if (newValue > upThreshold || newValue < downThreshold) { + color = cv::Scalar(0, 0, 255); + if (jugementEnable == true) { + halconOk = false; + defects.push_back(ngText); + } + } + _cvPainter.appendPaintJob(valueDisplayText + controlAccuracy(newValue, 4) + valueDanWei, p, color); +} +void valueJudgement(double value, valueControlStruct valueControlStructInput, double offset, + bool& halconOk, std::string valueDisplayText, std::string valueDanWei, std::string ngText, + cv::Point p, std::vector& defects, cvPainter& _cvPainter) { + cv::Scalar color = cv::Scalar(0, 255, 0); + double newValue = getPerfectOutputValue(value, valueControlStructInput.minThreshold, valueControlStructInput.maxThreshold, offset).second; + if (newValue > valueControlStructInput.maxThreshold || newValue < valueControlStructInput.minThreshold) { + color = cv::Scalar(0, 0, 255); + if (valueControlStructInput.enable == true) { + halconOk = false; + defects.push_back(ngText); + } + } + _cvPainter.appendPaintJob(valueDisplayText + controlAccuracy(newValue, 4) + valueDanWei, p, color); +} diff --git a/include/simpleAlgo.h b/include/simpleAlgo.h index 2bf7605..576a0eb 100644 --- a/include/simpleAlgo.h +++ b/include/simpleAlgo.h @@ -99,7 +99,7 @@ static cv::Mat cutImg2Cls(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targe // 期望在原图上的 ROI(可能越界) cv::Rect expectRect(mid.x - sideLength / 2, mid.y - sideLength / 2, sideLength, sideLength); - // 计算需要的边界填充(反射填充) + // 计算需要的边界填充(使用边缘平均颜色填充) const int left = std::max(0, -expectRect.x); const int top = std::max(0, -expectRect.y); const int right = std::max(0, expectRect.x + expectRect.width - imgInput.cols); @@ -107,7 +107,18 @@ static cv::Mat cutImg2Cls(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targe cv::Mat padded; if (left || top || right || bottom) { - cv::copyMakeBorder(imgInput, padded, top, bottom, left, right, cv::BORDER_REFLECT_101); + cv::Scalar fillColor; + if (imgInput.rows <= 1 || imgInput.cols <= 1) { + fillColor = cv::mean(imgInput); + } else { + cv::Mat borderMask = cv::Mat::zeros(imgInput.size(), CV_8UC1); + borderMask.row(0).setTo(255); + borderMask.row(borderMask.rows - 1).setTo(255); + borderMask.col(0).setTo(255); + borderMask.col(borderMask.cols - 1).setTo(255); + fillColor = cv::mean(imgInput, borderMask); + } + cv::copyMakeBorder(imgInput, padded, top, bottom, left, right, cv::BORDER_CONSTANT, fillColor); } else { padded = imgInput; } @@ -814,3 +825,7 @@ static inline std::string gbkToUtf8(const std::string& gbkStr) QByteArray utf8 = unicode.toUtf8(); return std::string(utf8.constData(), static_cast(utf8.size())); } + +static cv::Rect getMatRect(const cv::Mat& mat) { + return cv::Rect(0, 0, mat.cols, mat.rows); +} \ No newline at end of file diff --git a/include/simpleTools/windowsCtrl.hpp b/include/simpleTools/windowsCtrl.hpp index f0b43f3..5fc66a3 100644 --- a/include/simpleTools/windowsCtrl.hpp +++ b/include/simpleTools/windowsCtrl.hpp @@ -338,4 +338,46 @@ static bool restartProgram(bool asAdmin = false, bool exitCurrent = true, int de return true; } } + +struct memUsage { + DWORDLONG totalPhys;//总物理内存大小 + DWORDLONG availPhys;//可用物理内存大小 + DWORDLONG usedPhys;//已使用物理内存大小 + + DWORDLONG totalVir;//总虚拟内存大小 + DWORDLONG availVir;//可用虚拟内存大小 + DWORDLONG usedVir;//已使用虚拟内存大小 + + DWORDLONG totalMem;//总物理+虚拟内存大小 + DWORDLONG availMem;//可用物理+虚拟内存大小 + DWORDLONG usedMem;//已使用物理+虚拟内存大小 + + double usedPhysPercent;//已使用物理内存大小的百分比 + double usedMemPercent;//已使用物理+虚拟内存大小的百分比 +}; + +static memUsage getMemUsage() { + memUsage result; + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(memInfo); + + if (GlobalMemoryStatusEx(&memInfo)) + { + result.totalPhys = memInfo.ullTotalPhys; + result.availPhys = memInfo.ullAvailPhys; + result.usedPhys = result.totalPhys - result.availPhys; + + result.totalVir = memInfo.ullTotalPageFile - memInfo.ullTotalPhys; + result.availVir = memInfo.ullAvailPageFile - memInfo.ullAvailPhys; + result.usedPhys = result.totalVir - result.availVir; + + result.totalMem = memInfo.ullTotalPageFile; + result.availMem = memInfo.ullAvailPageFile; + result.usedMem = result.totalMem - result.availMem; + + result.usedPhysPercent = (double)result.usedPhys / (double)result.totalPhys; + result.usedMemPercent = (double)result.usedMem / (double)result.totalMem; + } + return result; +} #pragma once \ No newline at end of file diff --git a/include/sql/dataRecorder.h b/include/sql/dataRecorder.h new file mode 100644 index 0000000..7a753f5 --- /dev/null +++ b/include/sql/dataRecorder.h @@ -0,0 +1,252 @@ +#include +#pragma once + +class dataRecorder : public QThread { +public: + dataRecorder(SQLiteDB* dbIn, QList>externalInitDataVec = QList>()) : db(dbIn) { + db_InitColumns.append(externalInitDataVec); + } + ~dataRecorder() { + // 确保线程安全退出 + if (isRunning()) { + quit(); + wait(5000); // 等待最多5秒 + } + } + + std::map csvAndChartLink; + SQLiteDB* db; + + void addData(QString snCode, std::map data) { + if (snCode.isEmpty()) { + qWarning() << "addData: snCode is empty."; + return; + } + if (data.empty()) { + qWarning() << "addData: data is empty."; + return; + } + + { + QMutexLocker locker(&queueLock); + queue.enqueue(std::make_pair(snCode, data)); + } + + if (!isRunning()) { + start(); + } + } + + std::map getDataBySnCode(const QString& snCode) { + auto result = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} });; + return result.size() > 0 ? result[0] : std::map(); + } + +protected: + void run() override { + while (true) { + std::pair> val; + + // 从队列获取数据 + { + QMutexLocker locker(&queueLock); + if (queue.isEmpty()) { + break; + } + val = queue.dequeue(); + } + + try { + processData(val.first, val.second); + } + catch (const std::exception& ex) { + qCritical() << "addData: Exception caught:" << ex.what(); + } + catch (...) { + qCritical() << "addData: Unknown exception caught."; + } + } + } + +private: + // 统一锁顺序:先队列锁,再数据库锁 + QMutex queueLock; + QQueue>> queue; + + std::mutex _mutex; + int m_intervalMs = 10000; + QElapsedTimer timer; + QString currentProcessCsvNameCache; + nlohmann::json m_json; + QList> db_newCol = {}; + QList> db_InitColumns = { + {"条码", "TEXT NOT NULL PRIMARY KEY"}, + {"创建时间", "TEXT"}, + {"修改时间", "TEXT"}, + {"总流程耗时", "TEXT"}, + {"算法结果", "TEXT"}, + {"AI检测结果", "TEXT"}, + }; + + void processData(const QString& snCode, const std::map& data) { + QString currentProcessCsvName = getCurrentProcessCsvName(); + + // 确保表存在 + if (currentProcessCsvName != currentProcessCsvNameCache) { + if (!db->createTable(currentProcessCsvName, db_InitColumns)) { + qWarning() << "addData: Failed to create table:" << currentProcessCsvName; + return; + } + currentProcessCsvNameCache = currentProcessCsvName; + } + + // 检查并添加缺失的列 + ensureColumns(currentProcessCsvName, data); + + QString nowTime = QDateTime::currentDateTime().toString("yyyy_MM_dd hh:mm:ss.zzz"); + + // 检查数据是否存在 + auto nowRes = db->_search(currentProcessCsvName, {}, { {"条码", snCode} }); + + if (nowRes.empty()) { + //数据不存在 检查上一个班次的数据 + auto lastRes = db->_search(getLastProcessCsvName(), {}, { {"条码", snCode} }); + //数据依旧不存在 + if (lastRes.empty()) { + // 插入新数据 + std::map insertData = data; + insertData["条码"] = snCode; + insertData["创建时间"] = nowTime; + insertData["修改时间"] = nowTime; + + if (!db->_insert(currentProcessCsvName, insertData)) { + qWarning() << "addData: Insert failed for table:" << currentProcessCsvName << "snCode:" << snCode; + } + } + else {//数据在上一个班次 更新到上一个班次的数据 + + std::map updateData = data; + updateData["修改时间"] = nowTime; + if (lastRes[0]["算法结果"] == "NG") + updateData["算法结果"] = "NG"; + if (lastRes[0]["AI检测结果"] == "NG") { + updateData["AI检测结果"] = "NG"; + } + if (!db->_update(getLastProcessCsvName(), updateData, { {"条码", snCode} })) { + qWarning() << "addData: Update failed for table:" << getLastProcessCsvName() << "snCode:" << snCode; + } + } + } + else {//数据在当前班次 更新当前数据 + + std::map updateData = data; + updateData["修改时间"] = nowTime; + if (nowRes[0]["算法结果"] == "NG") { + updateData["算法结果"] = "NG"; + } + if (nowRes[0]["AI检测结果"] == "NG") { + updateData["AI检测结果"] = "NG"; + } + if (!db->_update(getCurrentProcessCsvName(), updateData, { {"条码", snCode} })) { + qWarning() << "addData: Update failed for table:" << getCurrentProcessCsvName() << "snCode:" << snCode; + } + } + } + + void ensureColumns(const QString& tableName, const std::map& data) { + // 获取当前表的列信息 + QList tableColumns; + auto columnsResult = db->query("PRAGMA table_info(\"" + tableName + "\")"); + + for (const auto& row : columnsResult) { + tableColumns.append(row.value("name").toString()); + } + + // 添加缺失的列 + for (const auto& kv : data) { + if (!tableColumns.contains(kv.first)) { + if (!db->addColumn(tableName, kv.first, "TEXT")) { + qWarning() << "addData: Failed to add column:" << kv.first << "to table:" << tableName; + } + } + } + } + + void init() { + std::lock_guard locker(_mutex); + + if (!db->createTable(getLastProcessCsvName(), db_InitColumns)) { + qDebug() << "Failed to create table " << getLastProcessCsvName(); + } + + currentProcessCsvNameCache = getCurrentProcessCsvName(); + if (!db->createTable(currentProcessCsvNameCache, db_InitColumns)) { + qDebug() << "Failed to create table " << currentProcessCsvNameCache; + } + + for (const auto& data : db_newCol) { + db->addColumn(getLastProcessCsvName(), data.first, data.second); + db->addColumn(getCurrentProcessCsvName(), data.first, data.second); + } + + timer.start(); + } + //获取当前班次的名字 + QString getCurrentProcessCsvName() { + QDateTime currentDateTime = QDateTime::currentDateTime(); + int hour = currentDateTime.time().hour(); + int minute = currentDateTime.time().minute(); + //判断是否为白班 + bool isDayShift = (hour > 8 || (hour == 8 && minute >= 30)) && (hour < 20 || (hour == 20 && minute <= 30)); + //判断是否为前一天 + bool isDayBefore = hour < 8 || (hour == 8 && minute <= 29); + + QString dateStr; + if (isDayBefore) { + dateStr = currentDateTime.addDays(-1).toString("yyyy-MM-dd"); + } + else { + dateStr = currentDateTime.toString("yyyy-MM-dd"); + } + + return dateStr + (isDayShift ? "白班" : "夜班"); + } + //获取上一个班次的名字 + QString getLastProcessCsvName() { + QDateTime currentDateTime = QDateTime::currentDateTime(); + int hour = currentDateTime.time().hour(); + int minute = currentDateTime.time().minute(); + //判断是否为白班 + bool isDayShift = (hour > 8 || (hour == 8 && minute >= 30)) && (hour < 20 || (hour == 20 && minute <= 30)); + //判断是否为前一天 + bool isDayBefore = hour < 8 || (hour == 8 && minute <= 29); + + QDateTime lastShiftDateTime; + //如果当前为前一天的夜班 那就返回前一天的白班 + if (isDayBefore) { + return currentDateTime.addDays(-1).toString("yyyy-MM-dd") + "白班"; + } + //如果当前为今天的白班 那就返回前一天的夜班 + else if (!isDayBefore && isDayShift) { + return currentDateTime.addDays(-1).toString("yyyy-MM-dd") + "夜班"; + } + //如果当前为今天的夜班 那就返回今天的白班 + return currentDateTime.addDays(-1).toString("yyyy-MM-dd") + "白班"; + } + + void exportCsv() { + QString name = getLastProcessCsvName(); + db->exportTableToCSV(name, "./csv/" + name + ".csv"); + + name = getCurrentProcessCsvName(); + db->exportTableToCSV(name, "./csv/" + name + ".csv"); + + QString currentProcessCsvName = getCurrentProcessCsvName(); + if (currentProcessCsvName != currentProcessCsvNameCache) { + if (db->createTable(currentProcessCsvName, db_InitColumns)) { + currentProcessCsvNameCache = currentProcessCsvName; + } + } + } +}; + diff --git a/include/sql/qSqlBase.h b/include/sql/qSqlBase.h index 3dce427..b4563ad 100644 --- a/include/sql/qSqlBase.h +++ b/include/sql/qSqlBase.h @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include class SQLiteDB { public: @@ -451,271 +454,4 @@ private: QString dbPath; }; -class DataRecorder : public QThread { -public: - DataRecorder (SQLiteDB* dbIn, QList>externalInitDataVec = QList>()) : db(dbIn) { - db_InitColumns.append(externalInitDataVec); - } - ~DataRecorder () { - // 确保线程安全退出 - if (isRunning()) { - quit(); - wait(5000); // 等待最多5秒 - } - } - - std::map csvAndChartLink; - SQLiteDB* db; - - void addData(QString snCode, std::map data) { - if (snCode.isEmpty()) { - qWarning() << "addData: snCode is empty."; - return; - } - if (data.empty()) { - qWarning() << "addData: data is empty."; - return; - } - - { - QMutexLocker locker(&queueLock); - queue.enqueue(std::make_pair(snCode, data)); - } - - if (!isRunning()) { - start(); - } - } - - bool isNewData(const QString& snCode) { - auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} }); - return res.empty(); - } - - bool isAlgoNgData(const QString& snCode) { - auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} }); - if (res.empty()) { - return false; - } - - if (res[0]["算法结果"].toString() == "NG") { - return true; - } - return false; - } - - bool isAiDetNgData(const QString& snCode) { - auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} }); - if (res.empty()) { - return false; - } - - if (res[0]["AI检测结果"].toString() == "NG") { - return true; - } - return false; - } - -protected: - void run() override { - while (true) { - std::pair> val; - - // 从队列获取数据 - { - QMutexLocker locker(&queueLock); - if (queue.isEmpty()) { - break; - } - val = queue.dequeue(); - } - - try { - processData(val.first, val.second); - } - catch (const std::exception& ex) { - qCritical() << "addData: Exception caught:" << ex.what(); - } - catch (...) { - qCritical() << "addData: Unknown exception caught."; - } - } - } - -private: - // 统一锁顺序:先队列锁,再数据库锁 - QMutex queueLock; - QQueue>> queue; - - std::mutex _mutex; - int m_intervalMs = 10000; - QElapsedTimer timer; - QString currentProcessCsvNameCache; - nlohmann::json m_json; - QList> db_newCol = {}; - QList> db_InitColumns = { - {"条码", "TEXT NOT NULL PRIMARY KEY"}, - {"创建时间", "TEXT"}, - {"修改时间", "TEXT"}, - {"CT", "TEXT"}, - {"算法结果", "TEXT"}, - {"AI检测结果", "TEXT"}, - }; - - void processData(const QString& snCode, const std::map& data) { - QString currentProcessCsvName = getCurrentProcessCsvName(); - - // 确保表存在 - if (currentProcessCsvName != currentProcessCsvNameCache) { - if (!db->createTable(currentProcessCsvName, db_InitColumns)) { - qWarning() << "addData: Failed to create table:" << currentProcessCsvName; - return; - } - currentProcessCsvNameCache = currentProcessCsvName; - } - - // 检查并添加缺失的列 - ensureColumns(currentProcessCsvName, data); - - QString nowTime = QDateTime::currentDateTime().toString("yyyy_MM_dd hh:mm:ss.zzz"); - - // 检查数据是否存在 - auto res = db->_search(currentProcessCsvName, {}, { {"条码", snCode} }); - - if (res.empty()) { - //数据不存在 检查上一个班次的数据 - auto lastRes = db->_search(getLastProcessCsvName(), {}, { {"条码", snCode} }); - //数据依旧不存在 - if (lastRes.empty()) { - // 插入新数据 - std::map insertData = data; - insertData["条码"] = snCode; - insertData["创建时间"] = nowTime; - insertData["修改时间"] = nowTime; - - if (!db->_insert(currentProcessCsvName, insertData)) { - qWarning() << "addData: Insert failed for table:" << currentProcessCsvName << "snCode:" << snCode; - } - } - else {//数据在上一个班次 更新到上一个班次的数据 - - std::map updateData = data; - updateData["修改时间"] = nowTime; - if (isAlgoNgData(snCode)) { - updateData["算法结果"] = "NG"; - } - if (isAiDetNgData(snCode)) { - updateData["AI检测结果"] = "NG"; - } - if (!db->_update(getLastProcessCsvName(), updateData, { {"条码", snCode} })) { - qWarning() << "addData: Update failed for table:" << getLastProcessCsvName() << "snCode:" << snCode; - } - } - } - else {//数据在当前班次 更新当前数据 - - std::map updateData = data; - updateData["修改时间"] = nowTime; - if (isAlgoNgData(snCode)) { - updateData["算法结果"] = "NG"; - } - if (isAiDetNgData(snCode)) { - updateData["AI检测结果"] = "NG"; - } - if (!db->_update(getCurrentProcessCsvName(), updateData, { {"条码", snCode} })) { - qWarning() << "addData: Update failed for table:" << getCurrentProcessCsvName() << "snCode:" << snCode; - } - } - } - - void ensureColumns(const QString& tableName, const std::map& data) { - // 获取当前表的列信息 - QList tableColumns; - auto columnsResult = db->query("PRAGMA table_info(\"" + tableName + "\")"); - - for (const auto& row : columnsResult) { - tableColumns.append(row.value("name").toString()); - } - - // 添加缺失的列 - for (const auto& kv : data) { - if (!tableColumns.contains(kv.first)) { - if (!db->addColumn(tableName, kv.first, "TEXT")) { - qWarning() << "addData: Failed to add column:" << kv.first << "to table:" << tableName; - } - } - } - } - - void init() { - std::lock_guard locker(_mutex); - - if (!db->createTable(getLastProcessCsvName(), db_InitColumns)) { - qDebug() << "Failed to create table " << getLastProcessCsvName(); - } - - currentProcessCsvNameCache = getCurrentProcessCsvName(); - if (!db->createTable(currentProcessCsvNameCache, db_InitColumns)) { - qDebug() << "Failed to create table " << currentProcessCsvNameCache; - } - - for (const auto& data : db_newCol) { - db->addColumn(getLastProcessCsvName(), data.first, data.second); - db->addColumn(getCurrentProcessCsvName(), data.first, data.second); - } - - timer.start(); - } - - QString getCurrentProcessCsvName() { - QDateTime currentDateTime = QDateTime::currentDateTime(); - int hour = currentDateTime.time().hour(); - int minute = currentDateTime.time().minute(); - bool isDayShift = (hour > 8 || (hour == 8 && minute >= 30)) && (hour < 20 || (hour == 20 && minute <= 30)); - bool isDayBefore = hour <= 8 || (hour == 8 && minute <= 29); - - QString dateStr; - if (isDayBefore) { - dateStr = currentDateTime.addDays(-1).toString("yyyy-MM-dd"); - } else { - dateStr = currentDateTime.toString("yyyy-MM-dd"); - } - - return dateStr + (isDayShift ? "白班" : "夜班"); - } - - QString getLastProcessCsvName() { - QDateTime currentDateTime = QDateTime::currentDateTime(); - int hour = currentDateTime.time().hour(); - int minute = currentDateTime.time().minute(); - bool isDayShift = (hour >= 8 && minute >= 30) && (hour <= 20 && minute <= 30); - bool isDayBefore = hour <= 20 && minute <= 30; - - QDateTime lastShiftDateTime; - if (isDayBefore) { - lastShiftDateTime = currentDateTime.addDays(-1); - } else { - lastShiftDateTime = currentDateTime; - } - - QString dateStr = lastShiftDateTime.toString("yyyy-MM-dd"); - return dateStr + (isDayShift ? "夜班" : "白班"); - } - - void exportCsv() { - QString name = getLastProcessCsvName(); - db->exportTableToCSV(name, "./csv/" + name + ".csv"); - - name = getCurrentProcessCsvName(); - db->exportTableToCSV(name, "./csv/" + name + ".csv"); - - QString currentProcessCsvName = getCurrentProcessCsvName(); - if (currentProcessCsvName != currentProcessCsvNameCache) { - if (db->createTable(currentProcessCsvName, db_InitColumns)) { - currentProcessCsvNameCache = currentProcessCsvName; - } - } - } -}; - #pragma once \ No newline at end of file diff --git a/include/subModuleBase.h b/include/subModuleBase.h index 6d1d391..5578be9 100644 --- a/include/subModuleBase.h +++ b/include/subModuleBase.h @@ -7,7 +7,7 @@ #include #include -class dsModuleBase +class dsModuleBase : public QObject { public: bool init_flag; //是否初始化,false,true @@ -22,6 +22,7 @@ public: virtual bool writeData(std::vector&,QByteArray&){return true;} virtual bool initParas(QByteArray&){return true;} virtual bool run() {return true;} + virtual bool checkStatus() { return true; }; }; class rpcModuleBase : public QObject diff --git a/include/sxGlobal.h b/include/sxGlobal.h index d0b491d..c2beabc 100644 --- a/include/sxGlobal.h +++ b/include/sxGlobal.h @@ -3,6 +3,7 @@ #include #include #include +#include #pragma region 基础函数 #define CATCH_EXCEPTIONS \ catch (HDevEngineCpp::HDevEngineException& ex) { \ @@ -140,20 +141,21 @@ public: std::string sn; for (auto& snAddr : address[batteryOffset].snAddr) { - sn.append(_serviceManager->_triggerManager->readSnFromQByteArray(byteArray, snAddr, true, false, -1)); + sn.append(_serviceManager->_triggerManager->readSnFromQByteArray(byteArray, snAddr, true, -1)); if (snAddr != address[batteryOffset].snAddr.back()) { sn.append("_"); } } auto camera = _serviceManager->_cameraManager->getCamera(cameraName);//获取相机 - camera->clearCache();//清除缓存 + //camera->clearCache();//清除缓存 三协机台此功能不通用 按需启动 std::shared_ptr data = _serviceManager->_detDataManager->createDetData( QString::fromStdString(cameraName), sn, oneTriggerCaptrueTimes, camera->config.oriSaveFormat);//创建数据包 data->resultImgShowSet.push_back(showImgPack(viewerName, "结果图")); data->saveObjectName = saveObjectName; camera->captureImage(data);//拍照 generalDet->asyncProcess(data, address[batteryOffset], ("D" + address[batteryOffset].completeAddr + "_1"));//处理算法 并在取到图片后发送完成信号 + qDebug() << data->uuid << "bind->" << QString::fromStdString(cameraName) << "|" << QString::fromStdString(sn) << "|" << address[batteryOffset].triggerAddr << "->" << address[batteryOffset].triggerValue; } private: QString saveObjectName = ""; diff --git a/include/yhGlobal.h b/include/yhGlobal.h index 02ff159..428b02c 100644 --- a/include/yhGlobal.h +++ b/include/yhGlobal.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #pragma region 基础函数 #define CATCH_EXCEPTIONS \ catch (HDevEngineCpp::HDevEngineException& ex) { \ @@ -59,6 +61,7 @@ public: }; #pragma endregion + //算法回调函数需要包含完整的算法流程以及发送信号,发送ui图表,捕获异常,处理异常等操作 //并且注意 异常可能会导致运行不到发送信号的函数,请在处理异常时发送异常结果 @@ -191,7 +194,7 @@ public: } //获取图片并进行检测 void evaluate(int batteryOffset, QByteArray& byteArray) { - std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, false, -1); + std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, -1); auto camera = _serviceManager->_cameraManager->getCamera(cameraName); camera->clearCache(); std::shared_ptr data = _serviceManager->_detDataManager->createDetData( @@ -203,10 +206,11 @@ public: generalDet->asyncProcess(data, address[batteryOffset]);//处理算法 data->resultImgShowSet.push_back(showImgPack(viewerName, "结果图")); _serviceManager->_triggerManager->writeShort("D" + address[0].completeAddr + "_1");//完成信号 固定只发在第0位 + qDebug() << data->uuid << "bind->" << QString::fromStdString(cameraName) << "|" << QString::fromStdString(sn) << "|" << address[batteryOffset].triggerAddr; } //获取图片然后废弃 一般为进站NG void captrueAndDiscard(int batteryOffset, QByteArray& byteArray) { - std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, false, -1); + std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, -1); auto camera = _serviceManager->_cameraManager->getCamera(cameraName); camera->clearCache(); std::shared_ptr data = _serviceManager->_detDataManager->createDetData(QString::fromStdString(cameraName), sn, oneTriggerCaptrueTimes, camera->config.oriSaveFormat);//创建数据包 @@ -250,6 +254,7 @@ public: paramTemplte2* detParamsIn, ServiceManager* _serviceManagerIn, bSignalHub* bridgeIn, typename detCallbackChain::processAlgoCallback _processAlgoCallback, QString saveObjectNameIN = "") { + viewerName = viewerNameIN; cameraName = cameraNameIn; _serviceManager = _serviceManagerIn; @@ -284,7 +289,7 @@ public: //获取图片并进行检测 void evaluate(int batteryOffset, QByteArray& byteArray) { - std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, false, -1); + std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, -1); auto camera = _serviceManager->_cameraManager->getCamera(cameraName); camera->clearCache(); std::shared_ptr data = _serviceManager->_detDataManager->createDetData( @@ -295,6 +300,7 @@ public: generalDet->asyncProcess(data, address[batteryOffset]);//处理算法 _serviceManager->_triggerManager->writeShort("D" + address[batteryOffset].completeAddr + "_1");//完成信号 根据偏移发送 + qDebug() << data->uuid << "bind->" << QString::fromStdString(cameraName) << "|" << QString::fromStdString(sn) << "|" << address[batteryOffset].triggerAddr; } private: QString saveObjectName = ""; diff --git a/include/yhGlobal_601.h b/include/yhGlobal_601.h index feebe7b..225bb15 100644 --- a/include/yhGlobal_601.h +++ b/include/yhGlobal_601.h @@ -191,7 +191,7 @@ public: } //获取图片并进行检测 void evaluate(int batteryOffset, QByteArray& byteArray) { - std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, false, -1); + std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, -1); auto camera = _serviceManager->_cameraManager->getCamera(cameraName); camera->clearCache(); std::shared_ptr data = _serviceManager->_detDataManager->createDetData( @@ -206,7 +206,7 @@ public: } //获取图片然后废弃 void captrueAndDiscard(int batteryOffset, QByteArray& byteArray) { - std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, false, -1); + std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, -1); auto camera = _serviceManager->_cameraManager->getCamera(cameraName); camera->clearCache(); std::shared_ptr data = _serviceManager->_detDataManager->createDetData(QString::fromStdString(cameraName), sn, oneTriggerCaptrueTimes, camera->config.oriSaveFormat);//创建数据包 @@ -284,7 +284,7 @@ public: //获取图片并进行检测 void evaluate(int batteryOffset, QByteArray& byteArray) { - std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, false, -1); + std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, -1); auto camera = _serviceManager->_cameraManager->getCamera(cameraName); camera->clearCache(); std::shared_ptr data = _serviceManager->_detDataManager->createDetData( diff --git a/resources/icon/aiModelManage_b.png.png b/resources/icon/aiModelManage_b.png.png new file mode 100644 index 0000000..cb73bf4 Binary files /dev/null and b/resources/icon/aiModelManage_b.png.png differ diff --git a/resources/icon/aiModelManage_w.png b/resources/icon/aiModelManage_w.png new file mode 100644 index 0000000..42b3e89 Binary files /dev/null and b/resources/icon/aiModelManage_w.png differ diff --git a/resources/icon/algoManage_b.png b/resources/icon/algoManage_b.png new file mode 100644 index 0000000..ba1760d Binary files /dev/null and b/resources/icon/algoManage_b.png differ diff --git a/resources/icon/algoManage_w.png b/resources/icon/algoManage_w.png new file mode 100644 index 0000000..494492f Binary files /dev/null and b/resources/icon/algoManage_w.png differ diff --git a/resources/resources.qrc b/resources/resources.qrc index 5e19a83..d3ac3f3 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -49,6 +49,10 @@ icon/biaoDingBanBiaoDing.png icon/jiuDianBiaoDingBiaoDing.png icon/viewOnly.png + icon/aiModelManage_b.png.png + icon/aiModelManage_w.png + icon/algoManage_b.png + icon/algoManage_w.png TongYongUiIcon/0.cameraModule.png diff --git a/src/Core/backendInterface.cpp b/src/Core/backendInterface.cpp index b2d220c..3fb9d20 100644 --- a/src/Core/backendInterface.cpp +++ b/src/Core/backendInterface.cpp @@ -1,5 +1,5 @@ #include - +#include Backend::~Backend() { if (_paramSyncManager != nullptr) { delete _paramSyncManager; @@ -25,25 +25,21 @@ void Backend::init(SQLiteDB* db, nlohmann::ordered_json& json) { externalInitDataVec.append(std::make_pair(QString::fromStdString(it.key()), QString::fromStdString(it.value().dump()))); } } - _dataRecorder = new DataRecorder(sqlDatabase, externalInitDataVec); - + _serviceManager._dataRecorder = new dataRecorder(sqlDatabase, externalInitDataVec); _serviceManager._detDataManager = new detDataManager(); - _missionResultHandle = new missionResultHandle(&_signalHub, _dataRecorder); - _serviceManager._missionResultHandle = _missionResultHandle; + _serviceManager._missionResultHandle = new missionResultHandle(&_signalHub, _serviceManager._dataRecorder); _serviceManager.dataBase = sqlDatabase; - _serviceManager._dataRecorder = _dataRecorder; _serviceManager._paramSyncManager = _paramSyncManager; - //CLI 只在首次初始化创建 - //connect(bridge, &bSignalHub::restartInit, this, &Backend::restartInit); - _cliManager = new CLIManager(&_serviceManager, &_signalHub); + _serviceManager._handleAiManager = new handleAiManager(&_signalHub); + _cliManager = new CLIManager(&_serviceManager, &_signalHub, _serviceManager._handleAiManager); initOnce = true; } // 创建/重建需要的服务模块,避免重复创建 if (!_serviceManager._imgSaveThreadManager) { - _serviceManager._imgSaveThreadManager = new imgSaveThreadManager(backendJson["imgSaveBasePath"],_paramSyncManager); + _serviceManager._imgSaveThreadManager = new imgSaveThreadManager(backendJson["imgSaveBasePath"],_paramSyncManager, &_signalHub); } if (!_serviceManager._cameraManager) { - _serviceManager._cameraManager = new cameraManager(backendJson, &_signalHub); + _serviceManager._cameraManager = new cameraManager(backendJson, &_signalHub, _paramSyncManager); } if (!_serviceManager._triggerManager) { _serviceManager._triggerManager = new triggerManager(&_serviceManager, &_signalHub, backendJson["Triggers"]); diff --git a/src/core/backendInterface.h b/src/core/backendInterface.h index 50e6dcb..58a9369 100644 --- a/src/core/backendInterface.h +++ b/src/core/backendInterface.h @@ -12,11 +12,9 @@ #include class Backend : public IBackend { public: - DataRecorder* _dataRecorder = nullptr;// 数据记录器 - missionResultHandle* _missionResultHandle = nullptr;// 任务结果处理器 ServiceManager _serviceManager;//服务类接口 paramSyncManager* _paramSyncManager = nullptr;//检测参数同步类//供客户在UI修改/查看 - CLIManager* _cliManager; // 命令行接口管理器 + CLIManager* _cliManager = nullptr;; // 命令行接口管理器 #if jsonRpcEnable rpc::JsonRpcServer _rpcServer; #endif @@ -96,11 +94,11 @@ public slots: mats->insert(mats->end(), matVec.begin(), matVec.end()); return; } - bool excuteCliCommand(const QString& command) { - return _cliManager->cliCommand(command); + bool ansyncExcuteCliCommand(const QString& command) { + return _cliManager->ansyncExcuteCliCommand(command); } - bool excuteCliCommandPreset(const QString& uuid) { - return _cliManager->excutePreset(uuid); + bool ansyncExcuteCliCommandPreset(const QString& uuid) { + return _cliManager->ansyncExcutePreset(uuid); } void getCameraNames(std::vector* cameraNames) { for (auto camera : _serviceManager._cameraManager->cameras) { @@ -114,6 +112,7 @@ private: nlohmann::ordered_json backendJson; SQLiteDB* sqlDatabase; MissionLoader _missionLoader; // 任务加载器,用于加载任务配置 + bool cameraInitResult = false, triggerInitResult = false; }; diff --git a/src/core/service/CLIManager.h b/src/core/service/CLIManager.h index bd689a0..5f0d785 100644 --- a/src/core/service/CLIManager.h +++ b/src/core/service/CLIManager.h @@ -4,16 +4,28 @@ #include #include -class CLIManager { +class CLIManager : public QObject { public: - CLIManager(ServiceManager* _serviceManagerIn,bSignalHub* bridgeIn) - :_serviceManager(_serviceManagerIn), bridge(bridgeIn) {} + CLIManager(ServiceManager* _serviceManagerIn, bSignalHub* bridgeIn, handleAiManager* _handleAiManagerIn) + :_serviceManager(_serviceManagerIn), bridge(bridgeIn), _handleAiManager(_handleAiManagerIn) { + QObject::connect(bridgeIn, &bSignalHub::Cli_addPreset, this, &CLIManager::addPreset); + } ~CLIManager() {} bool cliCommand(const QString& command) { + qInfo() << "执行指令:" << command; auto strList = command.split("#"); + int strListSize = strList.size(); + if (strListSize == 0) { + qWarning() << "执行指令时指令的大小为0"; + return false; + } + //执行具体的功能 if (strList[0] == "fins") { - _serviceManager->_triggerManager->sendFinsMsg(strList[1]); - return true; + if (strListSize > 1) { + _serviceManager->_triggerManager->sendFinsMsg(strList[1]); + return true; + } + return false; } else if (strList[0] == "reloadConfig") { _mission->reloadConfig(); @@ -23,18 +35,49 @@ public: QThread::msleep(strList[1].toInt()); return true; } + else if (strList[0] == "DirectTrigger") { + if (strListSize > 1) { + _serviceManager->_triggerManager->forceTriggerRegisteredCallback(strList[1].toStdString()); + } + return true; + } + else if (strList[0] == "modelManager") { + if (strListSize > 5) { + if (strList[1] == "update") { + std::string type = strList[2].toStdString(); + std::string oldModelPath = strList[3].toStdString(); + int batchSize = strList[4].toInt(); + std::string newModelPath = strList[5].toStdString(); + if (type == "seg") { + _handleAiManager->updateSegModel(oldModelPath, newModelPath, batchSize); + return true; + } + else if (type == "cls") { + _handleAiManager->updateClsModel(oldModelPath, newModelPath, batchSize); + return true; + } + else if (type == "det") { + _handleAiManager->updateDetModel(oldModelPath, newModelPath, batchSize); + return true; + } + } + } + return false; + } std::cout << "命令未定义!" << command.toStdString() << std::endl; return false; } - bool excutePreset(const QString& uuid) { + //处理预设命令 + bool ansyncExcutePreset(const QString& uuid) { auto commands = presets[uuid]; - std::thread([this,commands] {for (auto& command : commands)cliCommand(command); }).detach(); + std::thread([this, commands] {for (auto& command : commands)cliCommand(command); }).detach(); return true; } - void addPreset(const QString& btnName, QStringList commands) { - presets[btnName] = commands; - + bool ansyncExcuteCliCommand(const QString& command) { + std::thread([this, command] {cliCommand(command); }).detach(); + return true; } + //初始化基础预设 void initBasicPreset(mission* _missionIn) { //初始化单触发测试按钮 for (auto& data : _serviceManager->_triggerManager->triggerCallbackFuncs) { @@ -42,15 +85,32 @@ public: if (callbacks.funcName.find(avoid) != std::string::npos) { continue; } - QString uuid = QUuid::createUuid().toString();; - auto commands = basicTriggerPresetGenerate(callbacks.triggerAddress, callbacks.triggerValue); - QString tips; - for(auto& cmd : commands) { - tips += cmd + "\n"; - } - emit bridge->runCommand_addBtn( - QString::fromStdString(callbacks.funcName + "_" + std::to_string(callbacks.triggerAddress) + "_" + std::to_string(callbacks.triggerValue)), tips, uuid); - presets[uuid] = commands; +#pragma region 注册地址写入模式的 + { + QString uuid = QUuid::createUuid().toString();; + auto commands = basicTriggerPresetGenerate(callbacks.triggerAddress, callbacks.triggerValue); + QString tips; + for (auto& cmd : commands) { + tips += cmd + "\n"; + } + emit bridge->runCommand_addBtn(runCommandWidgetType::PlcSim, + QString::fromStdString(callbacks.funcName + "_" + std::to_string(callbacks.triggerAddress) + "_" + std::to_string(callbacks.triggerValue)), tips, uuid); + presets[uuid] = commands; + } +#pragma endregion +#pragma region 注册直接触发模式的 + { + QString uuid = QUuid::createUuid().toString();; + QStringList commands = { "DirectTrigger#"+ QString::fromStdString(callbacks.funcName)}; + QString tips; + for (auto& cmd : commands) { + tips += cmd + "\n"; + } + emit bridge->runCommand_addBtn(runCommandWidgetType::Direct, + QString::fromStdString(callbacks.funcName + "_" + std::to_string(callbacks.triggerAddress) + "_" + std::to_string(callbacks.triggerValue)), tips, uuid); + presets[uuid] = commands; + } +#pragma endregion } } //初始化联合触发测试按钮 @@ -59,15 +119,32 @@ public: if (callbacks.funcName.find(avoid) != std::string::npos) { continue; } - QString uuid = QUuid::createUuid().toString();; - auto commands = basicUnionTriggerPresetGenerate(callbacks.triggerAddressAndValue); - QString tips; - for (auto& cmd : commands) { - tips += cmd + "\n"; +#pragma region 注册地址写入模式的 + { + QString uuid = QUuid::createUuid().toString();; + auto commands = basicUnionTriggerPresetGenerate(callbacks.triggerAddressAndValue); + QString tips; + for (auto& cmd : commands) { + tips += cmd + "\n"; + } + emit bridge->runCommand_addBtn(runCommandWidgetType::PlcSim, + QString::fromStdString(callbacks.funcName), tips, uuid); + presets[uuid] = commands; } - emit bridge->runCommand_addBtn( - QString::fromStdString(callbacks.funcName), tips, uuid); - presets[uuid] = commands; +#pragma endregion +#pragma region 注册直接触发模式的 + { + QString uuid = QUuid::createUuid().toString();; + QStringList commands = { "DirectTrigger#" + QString::fromStdString(callbacks.funcName) }; + QString tips; + for (auto& cmd : commands) { + tips += cmd + "\n"; + } + emit bridge->runCommand_addBtn(runCommandWidgetType::Direct, + QString::fromStdString(callbacks.funcName), tips, uuid); + presets[uuid] = commands; + } +#pragma endregion } } //初始化重新加载配置文件按钮 @@ -75,7 +152,7 @@ public: QString uuid = QUuid::createUuid().toString();; QStringList commands = { "reloadConfig" }; QString tips = "reloadConfig"; - emit bridge->runCommand_addBtn( + emit bridge->runCommand_addBtn(runCommandWidgetType::PlcSim, QString::fromStdString("重新加载算法文件"), tips, uuid); presets[uuid] = commands; _mission = _missionIn; @@ -104,56 +181,87 @@ public: } for (auto& callbacks : unionCallbacks.triggerAddressAndValue) { commands.append("fins#write_short_D" + QString::number(callbacks.first) + "_" + QString::number(callbacks.second)); - tips += "fins#write_short_D" + QString::number(callbacks.second) + "_" + QString::number(callbacks.second) + "\n"; + tips += "fins#write_short_D" + QString::number(callbacks.first) + "_" + QString::number(callbacks.second) + "\n"; setZeroCommands.append("fins#write_short_D" + QString::number(callbacks.first) + "_0"); setZeroTips += "fins#write_short_D" + QString::number(callbacks.first) + "_0" + "\n"; } } } QString allTriggerUuid = QUuid::createUuid().toString(); - commands.append("waitMs#500"); + commands.append("waitMs#1000"); commands.append(setZeroCommands); presets[allTriggerUuid] = commands; - tips.append("waitMs#500\n"); + tips.append("waitMs#1000\n"); tips.append(setZeroTips); - emit bridge->runCommand_addBtn("全部触发", tips, allTriggerUuid); + emit bridge->runCommand_addBtn(runCommandWidgetType::PlcSim, "全部触发", tips, allTriggerUuid); QString allClearUuid = QUuid::createUuid().toString(); - emit bridge->runCommand_addBtn("触发全部清零", setZeroTips, allClearUuid); + emit bridge->runCommand_addBtn(runCommandWidgetType::PlcSim, "触发全部清零", setZeroTips, allClearUuid); presets[allClearUuid] = setZeroCommands; } } - QStringList basicTriggerPresetGenerate(int& triggerAddress,int triggerValue) { + //生成基础触发预设 + QStringList basicTriggerPresetGenerate(int& triggerAddress, int triggerValue) { QString triggerAddressStr = QString::number(triggerAddress); QString triggerValueStr = QString::number(triggerValue); return { - "fins#write_short_D"+ triggerAddressStr +"_" + triggerValueStr, - "waitMs#500", + "fins#write_short_D" + triggerAddressStr + "_" + triggerValueStr, + "waitMs#1000", "fins#write_short_D" + triggerAddressStr + "_0", }; } + //生成联合触发预设 QStringList basicUnionTriggerPresetGenerate(const std::vector>& triggerAddressAndValue) { - QStringList commands; - for (const auto& pair : triggerAddressAndValue) { - QString triggerAddressStr = QString::number(pair.first); - QString triggerValueStr = QString::number(pair.second); - commands << ("fins#write_short_D" + triggerAddressStr + "_" + triggerValueStr); - } - commands << "waitMs#500"; - for (const auto& pair : triggerAddressAndValue) { - QString triggerAddressStr = QString::number(pair.first); - commands << ("fins#write_short_D" + triggerAddressStr + "_0"); - } - return commands; - } + QStringList commands; + //计算触发地址的间距 + bool registerAsSingle = false; + std::vector vec; + if (triggerAddressAndValue.size() >= 2) {//只有大于一个才进特殊处理,不然当单触发处理 + for (int i = 1; i < triggerAddressAndValue.size(); i++) {//计算地址间距 + int value = triggerAddressAndValue[i].first - triggerAddressAndValue[i - 1].first; + if (value < 0) {//还得排序.. + registerAsSingle = true; + break; + } + vec.push_back(value); + } + } + else { + registerAsSingle = true; + } + + if (registerAsSingle) { + for (const auto& pair : triggerAddressAndValue) { + QString triggerAddressStr = QString::number(pair.first); + QString triggerValueStr = QString::number(pair.second); + commands << ("fins#write_short_D" + triggerAddressStr + "_" + triggerValueStr); + } + } + else { + QString command = ("fins#write_short_D" + QString::number(triggerAddressAndValue[0].first) + "_" + QString::number(triggerAddressAndValue[0].second)); + for (int i = 0; i < vec.size(); i++) { + for (int o = 0; o < vec[i] - 1; o++) + command.append(",0"); + command.append("," + QString::number(triggerAddressAndValue[i + 1].second)); + } + commands << command; + } + commands << "waitMs#1000"; + for (const auto& pair : triggerAddressAndValue) { + QString triggerAddressStr = QString::number(pair.first); + commands << ("fins#write_short_D" + triggerAddressStr + "_0"); + } + return commands; + } private: mission* _mission; bSignalHub* bridge; QMap presets; ServiceManager* _serviceManager; + handleAiManager* _handleAiManager; enum CommandType { Trigger, Evaluate @@ -163,6 +271,12 @@ private: {"evaluate", Evaluate} }; std::string avoid = "清零"; +public slots: + void addPreset(QString btnName, QString tips, QStringList commands) { + QString uuid = QUuid::createUuid().toString(); + presets[uuid] = commands; + emit bridge->runCommand_addBtn(runCommandWidgetType::PlcSim, btnName, tips, uuid); + } }; #pragma once \ No newline at end of file diff --git a/src/core/service/cameraManager.cpp b/src/core/service/cameraManager.cpp index 34002b5..5fdb43e 100644 --- a/src/core/service/cameraManager.cpp +++ b/src/core/service/cameraManager.cpp @@ -57,17 +57,29 @@ static MV_GENTL_IF_INFO_LIST searchMV_GENTL_IF_INFO_LIST() } return m_stIFList; } -camera::camera(cameraStruct configIn, int indexIn, bSignalHub* bridgeIn){ +camera::camera(cameraStruct configIn, int indexIn, + bSignalHub* bridgeIn, + cameraManager* _cameraManagerIn, + paramSyncManager* _paramSyncManagerIn){ + + _cameraManager = _cameraManagerIn; bridge = bridgeIn; config = configIn; cameraName = QString::fromStdString(config.key); + _paramSyncManager = _paramSyncManagerIn; + _paramSyncManager->edit("本地读图", "多个路径用#分割,数量不足补充黑图", ""); + refreshParam(); index = indexIn; lastUpdateTime.start(); this->start(); } + void camera::run(){ QThread::msleep(2000); std::vectorcamera_mat; + int checkConnectinCount = 0;//用于判断是否改检测相机在线情况的计数器 + bool lastOnlineStatus = true;//用于判断相机的在线状态 + readLocalImgTimer.start(); while (runFlag){ int queueSize = requestQueue.size(); if (queueSize > 0){ @@ -76,35 +88,106 @@ void camera::run(){ QMutexLocker locker(&mutex); data = requestQueue.dequeue(); } - if(data->getOriImgSize() >= data->imgAmountMin)continue; - while (1) { - trigger(); - std::vector mats; - getCameraImg(mats, data->imgAmountMin); - data->insertOriImg(mats); - if (data->getOriImgSize() >= data->imgAmountMin) { - qDebug() << QString::fromStdString(data->SNcode) << data->uuid << " Min:" << data->imgAmountMin << " Get:" << data->getOriImgSize(); - break; + if (data->getOriImgSize() >= data->imgAmountMin) { + qDebug() << data->uuid << "容器内图片已达所需上限,此次任务已跳过"; + continue; + } + //每5秒同步参数 + refreshParam(); + //判断是否使用本地读图替代在线取图 + if (readLocalImgEnableOnce || readLocalImgEnableMulti) { + //获取路径 + QStringList imgPathList; + if (readLocalImgPath.contains("#")) + imgPathList = readLocalImgPath.split("#"); + else + imgPathList.append(readLocalImgPath); + //读取图片 + for (auto& path : imgPathList) { + cv::Mat mat = cv::imread(path.toStdString(), -1); + if (mat.empty()) + data->insertOriImg(cv::Mat(5, 5, CV_8UC1, cv::Scalar(0))); + else + data->insertOriImg(mat); + } + //判断是否满足数量,不满足补充黑图 + while (data->getOriImgSize() < data->imgAmountMin) { + data->insertOriImg(cv::Mat(5, 5, CV_8UC1, cv::Scalar(0))); + } + _paramSyncManager->edit("本地读图", cameraName + "启用本地读图(单次)", false); + qDebug() << data->uuid << "使用本地读图结束"; + } + else { + while (1) { + trigger(); + std::vector mats; + getCameraImg(mats, data->imgAmountMin); + data->insertOriImg(mats); + if (data->getOriImgSize() >= data->imgAmountMin) { + qDebug() << QString::fromStdString(data->SNcode) << data->uuid << " Min:" << data->imgAmountMin << " Get:" << data->getOriImgSize(); + break; + } } } } QThread::msleep(10); + if (++checkConnectinCount > 10) { + auto onlineStatus = checkStatus(); + if (onlineStatus != lastOnlineStatus){//状态变更 + _cameraManager->updateCameraOnlineStatus(onlineStatus, cameraName); + if (onlineStatus == false) { + std::cout << cameraName.toStdString() << "相机状态变更:掉线" << std::endl; + qDebug() << cameraName << "相机状态变更:掉线"; + } + else { + std::cout << cameraName.toStdString() << "相机状态变更:在线" << std::endl; + qDebug() << cameraName << "相机状态变更:在线"; + } + } + lastOnlineStatus = onlineStatus; + checkConnectinCount = 0; + } + } } cameraManager::~cameraManager(){ - for (auto camera : cameras) { - if (camera.second != nullptr) - delete camera.second; - } - //QThread::msleep(200); - for (auto& module : _cameraStructs) { - if (module.module_ptr != nullptr) { - delete module.module_ptr; - module.module_ptr = nullptr; + unloadAllCameras(); +} + +void cameraManager::unloadAllCameras(){ + for (auto& cameraItem : cameras) { + if (cameraItem.second != nullptr) { + cameraItem.second->stop(); + delete cameraItem.second; } } + cameras.clear(); + for (auto& module : _cameraStructs) { + module.init = false; + if (module.module_ptr != nullptr) { + if (module.destory_ptr != nullptr) + module.destory_ptr(module.module_ptr); + else + delete module.module_ptr; + module.module_ptr = nullptr; + } + module.destory_ptr = nullptr; + } + + for (auto* lib : lib_list) { + if (lib != nullptr) { + if (lib->isLoaded()) + lib->unload(); + delete lib; + } + } + lib_list.clear(); + m_usedDllPath.clear(); + onlineStatusMap.clear(); + initFlag = false; + status = NotInit; } void cameraManager::run(){ @@ -168,7 +251,7 @@ void cameraManager::run(){ continue; QByteArray initByte = cameraStruct.m_initByte; if (cameraStruct.module_ptr->initParas(initByte)){ - camera* _camera = new camera(cameraStruct, cameras.size(), bridge); + camera* _camera = new camera(cameraStruct, cameras.size(), bridge, this, _paramSyncManager); cameras.insert(std::make_pair(cameraStruct.key, _camera)); emit bridge->loadingWindow_appendInitLog("相机" + QString::fromStdString(cameraStruct.key) + "初始化成功", "green"); qDebug() << "[INFO] " << " initParas success!"; @@ -189,6 +272,15 @@ void cameraManager::run(){ else status = InitFailure; } + +void cameraManager::reloadAllCameraAsVirtual(){ + unloadAllCameras(); + for (auto& cameraStruct : _cameraStructs) + cameraStruct.dllPath = "Hd_VirtualCamera"; + run(); + return ; +} + bool cameraManager::loadModule(cameraStruct& _cameraStruct){ try{ QString keyName = QString::fromStdString(_cameraStruct.key); diff --git a/src/core/service/cameraManager.h b/src/core/service/cameraManager.h index 16aca55..16da15e 100644 --- a/src/core/service/cameraManager.h +++ b/src/core/service/cameraManager.h @@ -11,6 +11,8 @@ #include #include #include +#include + typedef dsModuleBase* (*CreateFunction)(); typedef void (*ReleaseFunction)(dsModuleBase* animal); @@ -31,6 +33,7 @@ struct cameraStruct { //硬触发要求在触发相机前给出触发信号 class backEndManager; +class cameraManager; class camera : public QThread{ Q_OBJECT public: @@ -38,7 +41,10 @@ public: QString cameraName; int imgShowCnt = 0; cameraStruct config; - camera(cameraStruct configIn, int indexIn, bSignalHub* bridgeIn); + camera(cameraStruct configIn, int indexIn, + bSignalHub* bridgeIn, + cameraManager* cameraManagerIn, + paramSyncManager* _paramSyncManagerIn); ~camera(){ runFlag = false; this->terminate(); @@ -63,7 +69,12 @@ public: } return true; } - double getCameraValue(){//点激光相机专用 + //获取相机的在线状态 true为在线 + bool checkStatus() { + return config.module_ptr->checkStatus(); + } + //点激光相机专用 获取测距的值 + double getCameraValue(){ double value = 0; std::vector camera_mat; QByteArray task_name; @@ -78,6 +89,7 @@ public: value = task_name.toDouble(); return value; } + //从相机模块获取图片 void getCameraImg(std::vector& mats,int imgAmountMin){ QByteArray task_name; if (!config.module_ptr->readData(mats, task_name)) { @@ -88,16 +100,32 @@ public: } qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera_mat size:" << mats.size(); } + //设置增益 inline void setDigitalShift(float value){ writeData("DigitalShift_" + std::to_string(value)); } + //设置曝光 inline void setExposureTime(float value){ writeData("exposure_" + std::to_string(value)); } + //清除缓存图片 inline void clearCache() { writeData("clearCache"); } + //同步参数 + void refreshParam() { + if (readLocalImgTimer.elapsed() > 5000) { + readLocalImgPath = _paramSyncManager->get("本地读图", cameraName + "本地读图路径", "").toString(); + readLocalImgEnableOnce = _paramSyncManager->get("本地读图", cameraName + "启用本地读图(单次)", false).toBool(); + readLocalImgEnableMulti = _paramSyncManager->get("本地读图", cameraName + "启用本地读图(多次)", false).toBool(); + } + } private: +#pragma region 本地读图部分 + QElapsedTimer readLocalImgTimer; + bool readLocalImgEnableOnce = false, readLocalImgEnableMulti = false; + QString readLocalImgPath = ""; +#pragma endregion bSignalHub* bridge; QByteArray readData(const std::string& str){ QByteArray byteArray(QByteArray::fromStdString(str)); @@ -119,11 +147,16 @@ private: QQueueimgQueue; QQueue>requestQueue; bool runFlag = true; + cameraManager* _cameraManager = nullptr; + paramSyncManager* _paramSyncManager = nullptr; }; class cameraManager : public serviceModule{ Q_OBJECT public: - cameraManager(nlohmann::ordered_json& json, bSignalHub* bridgeIn) { + cameraManager(nlohmann::ordered_json& json, bSignalHub* bridgeIn, + paramSyncManager* _paramSyncManagerIn) { + + _paramSyncManager = _paramSyncManagerIn; bridge = bridgeIn; auto cameraJson = json["CameraSetting"]; for (auto data : cameraJson){ @@ -144,9 +177,11 @@ public: } _cameraStructs.push_back(_cameraStruct); } + QObject::connect(bridgeIn, &bSignalHub::reloadAllCameraAsVirtual, this, &cameraManager::reloadAllCameraAsVirtual); } ~cameraManager(); void run(); + void reloadAllCameraAsVirtual(); std::unordered_mapcameras;//key 相机名 camera* getCamera(std::string cameraName){ auto camera = cameras.find(cameraName); @@ -158,12 +193,31 @@ public: for (auto camera : cameras) camera.second->stop(); } + void updateCameraOnlineStatus(const bool& isOnline, const QString& cameraKey) { + QMutexLocker locker(&onlineStatusMutex); + onlineStatusMap[cameraKey] = isOnline;//先改变其在线状态 + //再遍历是否还有掉线相机 + for (auto& value : onlineStatusMap) { + if (!value) { + emit bridge->setCamConnectionError(); + return; + } + } + emit bridge->setCamConnectionOk(); + return; + } private: + paramSyncManager* _paramSyncManager; +#pragma region 上线掉线处理 + QMutex onlineStatusMutex; + QMaponlineStatusMap; +#pragma endregion bool initFlag = false; std::vector _cameraStructs;//相机配置结构体 bSignalHub* bridge; bool loadModule(cameraStruct& _cameraStruct); + void unloadAllCameras(); std::vector vec_mat;//没啥用,一直保持为空就好 QList lib_list; std::map m_usedDllPath; diff --git a/src/core/service/detCallbackChain.hpp b/src/core/service/detCallbackChain.hpp index fbe1544..0b3acc4 100644 --- a/src/core/service/detCallbackChain.hpp +++ b/src/core/service/detCallbackChain.hpp @@ -130,7 +130,7 @@ public: QString totalCtStr = data->uuid + QString::fromStdString(data->SNcode) + "algoCT->" + QString::number(data->algoTime) + "ms|SN" + QString::fromStdString(data->SNcode); qDebug() << totalCtStr; // 将耗时数据等保存入数据库或日志 - data->sqlVec.insert({ "CT", QString::number(data->algoTime) + "ms" }); + data->sqlVec.insert({ "总流程耗时", QString::number(data->algoTime) + "ms" }); data->sqlVec.insert({ "算法结果", data->halconOK == false ? "NG" : "OK" }); data->sqlVec.insert({ "AI检测结果", data->aiDetOK == false ? "NG" : "OK" }); data->service->_dataRecorder->addData(QString::fromStdString(data->SNcode), data->sqlVec); diff --git a/src/core/service/detData.h b/src/core/service/detData.h index d1eba91..3b96c93 100644 --- a/src/core/service/detData.h +++ b/src/core/service/detData.h @@ -53,13 +53,13 @@ public: QMutexLocker locker(&resultMutex); return resultImgs.size(); } - const cv::Mat& getResultImg(const int index) { + cv::Mat getResultImg(const int index) { QMutexLocker locker(&resultMutex); if (resultImgs.size() - 1 < index) return cv::Mat(100, 100, CV_8UC1,cv::Scalar(0)); return resultImgs[index]; } - const std::vector& getResultImgVec() { + std::vector getResultImgVec() { QMutexLocker locker(&resultMutex); return resultImgs; } @@ -84,7 +84,7 @@ public: QMutexLocker locker(&resultMutex); return segOriginImgs.size(); } - const std::vector& getSegOriginImgVec() { + std::vector getSegOriginImgVec() { QMutexLocker locker(&resultMutex); return segOriginImgs; } @@ -109,7 +109,7 @@ public: QMutexLocker locker(&resultMutex); return clsOriginImgs.size(); } - const std::vector>& getClsOriginImgVec() { + std::vector> getClsOriginImgVec() { QMutexLocker locker(&resultMutex); return clsOriginImgs; } @@ -130,17 +130,13 @@ public: QMutexLocker locker(&oriImgMutex); return oriImgs.size(); } - std::vector* getOriImgVecPtr() { - QMutexLocker locker(&oriImgMutex); - return &oriImgs; - } - const cv::Mat& getOriImg(const int& index) { + cv::Mat getOriImg(const int& index) { QMutexLocker locker(&oriImgMutex); if (oriImgs.size() - 1 < index) return cv::Mat(100, 100, CV_8UC1, cv::Scalar(0)); return oriImgs[index]; } - const std::vector& getOriImgVec() { + std::vector getOriImgVec() { QMutexLocker locker(&oriImgMutex); return oriImgs; } @@ -160,14 +156,15 @@ public: oriImgs.erase(oriImgs.begin() + index); return true; } + #pragma endregion #pragma region 结果相关 bool aiDetOK = true; bool halconOK = true; std::vector resultSaveText; - void setNg(bool aiDetOK, bool halconOK, std::vector saveText) { - aiDetOK = aiDetOK; - halconOK = halconOK; + void setNg(bool aiDetOKIn, bool halconOKIn, std::vector saveText) { + aiDetOK = aiDetOKIn; + halconOK = halconOKIn; resultSaveText = saveText; } //结果图展示预设 diff --git a/src/core/service/halconGuard.h b/src/core/service/halconGuard.h new file mode 100644 index 0000000..81b9cf6 --- /dev/null +++ b/src/core/service/halconGuard.h @@ -0,0 +1,712 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//此类用于防止因为输入输出与halcon段不同以及halcon算法崩溃引起的内存泄露 + +class halconEngineManager{ +public: + using reloadCallback = std::function()>; + struct AlgoInfo { + std::atomic totalTime = 0.0, maxCostTime = 0.0, minCostTime = 999999.0; + std::atomic callCount = 0; + // 平均耗时(毫秒) + double avgTime() const { return callCount > 0 ? totalTime / callCount : 0.0; } + }; + // 获取单例 + static halconEngineManager& instance() { + static halconEngineManager inst; + return inst; + } + + // 加载算法文件,通知所有已注册的evaluater重新自检 + void loadAlgoFile(const std::string& algoFilePath, bSignalHub* bridgeIn) { + currentAlgoFilePath = algoFilePath; + if (bridgeIn != nullptr && bridge == nullptr) { + bridge = bridgeIn; + QObject::connect(bridge, &bSignalHub::backupAndReplaceAlgoFile, [this](const std::string& path) { + this->backupAndReplaceAlgoFile(path); + }); + } +#pragma region 注册缓存的耗时监测界面 + { + QMutexLocker locker(&m_mutex); + for (auto& kv = timeCostViewerMap.begin(); kv != timeCostViewerMap.end(); kv++) { + if (kv.value()) + bridge->halconManager_addTimeCostViewer(kv.key(), { "平均耗时","最大耗时","最小耗时" }); + } + timeCostViewerMap.clear(); + } +#pragma endregion +#pragma region 检查文件是否存在,如果不存在则阻塞并等待下一步操作 + if (!std::filesystem::exists(algoFilePath)) { + inited = false; + int retryTimes = 0; + if (bridge != nullptr) { + std::shared_ptr> recivedResult = std::make_shared>(false); + std::shared_ptr> result = std::make_shared>(-1); + emit bridge->waitForFrontendInitComfirm("找不到" + QString::fromStdString(algoFilePath) + "请检查后点击重试!", { "重试","跳过" }, recivedResult, result); + while (1) { + while (recivedResult->load() == false) + QThread::msleep(100); + recivedResult->store(false); + if (result->load() == 0) {//重试 + if (std::filesystem::exists(algoFilePath)) + break; + else { + emit bridge->waitForFrontendInitComfirm("依旧找不到" + QString::fromStdString(algoFilePath) + "请检查后点击重试!重试次数" + QString::number(retryTimes++), {"重试","取消"}, recivedResult, result); + } + } + else {//取消 + qWarning() << "错误: 算法文件不存在" << QString::fromStdString(algoFilePath); + break; + } + } + } + else { + qWarning() << "错误:文件不存在!" << QString::fromStdString(algoFilePath); + std::cout << "错误:" << algoFilePath << "文件不存在!"; + } + } +#pragma endregion + + std::map callbacksCopy; + { + QMutexLocker locker(&m_mutex); + HDevEngineCpp::HDevEngine myEngine; + myEngine.SetProcedurePath(" "); + myEngine.UnloadAllProcedures(); + myEngine.SetProcedurePath(algoFilePath.c_str()); + callbacksCopy = m_reloadCallbacks; + inited = true; + } + for (auto& kv : callbacksCopy) { + auto result = kv.second(); + if (!result.first) + qWarning() << "错误: reloadCallback failed:" << QString::fromStdString(result.second); + } + qDebug() << "已成功加载算法文件" << QString::fromStdString(algoFilePath); + std::cout << "已成功加载算法文件" << algoFilePath << std::endl; + } + // 备份当前算法文件并替换为新文件 + void backupAndReplaceAlgoFile(const std::string& oldAlgoFilePath) { + if (currentAlgoFilePath.empty()) { + qWarning() << "错误:当前未加载算法文件,无法替换"; + std::cout << "错误:当前未加载算法文件,无法替换" << std::endl; + return; + } + + QFileInfo targetInfo(QString::fromStdString(currentAlgoFilePath)); + QFileInfo sourceInfo(QString::fromStdString(oldAlgoFilePath)); + if (sourceInfo.filePath().isEmpty()) { + qWarning() << "错误:新算法文件路径为空"; + std::cout << "错误:新算法文件路径为空" << std::endl; + return; + } + + if (!sourceInfo.exists()) { + qWarning() << "错误:新算法文件不存在:" << sourceInfo.filePath(); + std::cout << "错误:新算法文件不存在: " << sourceInfo.filePath().toStdString() << std::endl; + return; + } + + if (sourceInfo.isDir()) { + QDir sourceDir(sourceInfo.filePath()); + QFileInfo candidate(sourceDir.filePath(targetInfo.fileName())); + if (candidate.exists() && candidate.isFile()) { + sourceInfo = candidate; + } + else { + bool found = false; + const auto fileInfos = sourceDir.entryInfoList({ "*.hdpl", "*.HDPL" }, QDir::Files | QDir::NoSymLinks, QDir::Name); + for (const auto& fileInfo : fileInfos) { + if (fileInfo.isFile()) { + sourceInfo = fileInfo; + found = true; + break; + } + } + if (!found) { + qWarning() << "错误:目录中未找到可替换的 .hdpl 文件:" << sourceDir.path(); + std::cout << "错误:目录中未找到可替换的 .hdpl 文件: " << sourceDir.path().toStdString() << std::endl; + return; + } + } + } + + if (!targetInfo.exists() || !targetInfo.isFile()) { + qWarning() << "错误:当前算法文件不存在,无法备份:" << targetInfo.filePath(); + std::cout << "错误:当前算法文件不存在,无法备份: " << targetInfo.filePath().toStdString() << std::endl; + return; + } + + QDir targetDir = targetInfo.dir(); + const QString backupRootPath = targetDir.filePath("backup"); + QDir backupRootDir; + if (!backupRootDir.mkpath(backupRootPath)) { + qWarning() << "错误:创建算法备份根目录失败:" << backupRootPath; + std::cout << "错误:创建算法备份根目录失败: " << backupRootPath.toStdString() << std::endl; + return; + } + + const QString backupDirPath = QDir(backupRootPath).filePath(targetInfo.fileName() + QDateTime::currentDateTime().toString("yyyy_MM_dd_hh-mm-ss")); + if (!backupRootDir.mkpath(backupDirPath)) { + qWarning() << "错误:创建算法备份目录失败:" << backupDirPath; + std::cout << "错误:创建算法备份目录失败: " << backupDirPath.toStdString() << std::endl; + return; + } + + const QString targetFilePath = targetInfo.filePath(); + const QString sourceFilePath = sourceInfo.filePath(); + const QString backupFilePath = QDir(backupDirPath).filePath(targetInfo.fileName()); + QFile targetFile(targetFilePath); + if (!targetFile.rename(backupFilePath)) { + if (!QFile::copy(targetFilePath, backupFilePath)) { + qWarning() << "错误:备份算法文件失败:" << targetFilePath; + std::cout << "错误:备份算法文件失败: " << targetFilePath.toStdString() << std::endl; + return; + } + if (!targetFile.remove()) { + qWarning() << "错误:删除旧算法文件失败:" << targetFilePath; + std::cout << "错误:删除旧算法文件失败: " << targetFilePath.toStdString() << std::endl; + QFile::rename(backupFilePath, targetFilePath); + return; + } + } + + if (QFile::exists(targetFilePath) && !QFile::remove(targetFilePath)) { + qWarning() << "错误:清理旧目标文件失败:" << targetFilePath; + std::cout << "错误:清理旧目标文件失败: " << targetFilePath.toStdString() << std::endl; + QFile::rename(backupFilePath, targetFilePath); + return; + } + + if (!QFile::copy(sourceFilePath, targetFilePath)) { + qWarning() << "错误:替换算法文件失败:" << sourceFilePath; + std::cout << "错误:替换算法文件失败: " << sourceFilePath.toStdString() << std::endl; + QFile::remove(targetFilePath); + if (!QFile::rename(backupFilePath, targetFilePath)) { + qWarning() << "错误:回滚算法文件失败:" << backupFilePath; + std::cout << "错误:回滚算法文件失败: " << backupFilePath.toStdString() << std::endl; + } + return; + } + + qInfo() << "[halconEngineManager] 算法文件已备份到" << backupFilePath; + qInfo() << "[halconEngineManager] 算法文件已替换为" << sourceFilePath; + loadAlgoFile(currentAlgoFilePath, bridge); + } + // 注册reload回调,返回ID用于注销 + int registerReloadCallback(reloadCallback cb) { + QMutexLocker locker(&m_mutex); + int id = m_nextCallbackId++; + m_reloadCallbacks[id] = cb; + return id; + } + + // 注销reload回调 + void unregisterReloadCallback(int id) { + QMutexLocker locker(&m_mutex); + m_reloadCallbacks.erase(id); + } + + + // 提交一次耗时(ms),累计计算平均耗时 + void submitTime(const std::string& funcName, double elapsed) { + QMutexLocker locker(&m_mutex); + auto& info = m_verifiedMap[funcName]; + info.totalTime = info.totalTime + elapsed; + info.callCount++; + info.maxCostTime = std::max((double)info.maxCostTime, elapsed); + info.minCostTime = std::min((double)info.minCostTime, elapsed); + //每十次发送一次平均耗时到界面 每千次输出一次日志并重置数据 + if (info.callCount % 10 == 0) { + auto it = m_verifiedMap.find(funcName); + auto avgTime = it != m_verifiedMap.end() ? it->second.avgTime() : 0.0; + bridge->halconManager_refreshTimeCostViewer(QString::fromStdString(funcName), QString::number(avgTime, 'f', 2) + "ms", QString::number(info.maxCostTime, 'f', 2) + "ms", QString::number(info.minCostTime, 'f', 2) + "ms"); + if (info.callCount == 1000) { + qInfo() << QString::fromStdString(funcName) + "近千次平均耗时" << avgTime << "ms,最高" << info.maxCostTime << "ms,最低" << info.minCostTime << "ms"; + info.callCount = 0; + info.totalTime = 0; + info.maxCostTime = 0; + info.minCostTime = 0; + } + } + } + + void registerTimeCostViewer(const QString& groupName) { + if (bridge) { + bridge->halconManager_addTimeCostViewer(groupName, { "平均耗时","最大耗时","最小耗时" }); + } + else { + QMutexLocker locker(&m_mutex); + timeCostViewerMap.insert(groupName, true); + } + } + // 获取某函数的平均耗时(ms),不存在返回0 + double getAvgTime(const std::string& funcName) const { + QMutexLocker locker(&m_mutex); + auto it = m_verifiedMap.find(funcName); + return it != m_verifiedMap.end() ? it->second.avgTime() : 0.0; + } + + bool isInited() const { + return inited.load(); + } + + bSignalHub* getSignalHub() { + return bridge; + } +private: + std::string currentAlgoFilePath; + QMap timeCostViewerMap; + bSignalHub* bridge = nullptr; + halconEngineManager() = default; + halconEngineManager(const halconEngineManager&) = delete; + halconEngineManager& operator=(const halconEngineManager&) = delete; + + mutable QMutex m_mutex; + std::map m_verifiedMap; + std::map m_reloadCallbacks; + std::atomic m_nextCallbackId = 0; + std::atomic inited{ false }; + +}; + + +class halconHTupleVectorMap { +public: + // 就地构造:key 不存在时插入,已存在时不覆盖,返回对应值的引用 + HalconCpp::HTupleVector& emplace(const std::string& key, const HalconCpp::HTupleVector& val = HalconCpp::HTupleVector{0}) { + auto [it, inserted] = m_map.emplace(key, val); + return it->second; + } + + // [] 读写:key 不存在时返回默认空 HTupleVector + HalconCpp::HTupleVector& operator[](const std::string& key) { + + auto [it, inserted] = m_map.emplace(key, HalconCpp::HTupleVector{ 0 }); + return it->second; + } + + bool contains(const std::string& key) const { + return m_map.find(key) != m_map.end(); + } + + auto begin() { return m_map.begin(); } + auto end() { return m_map.end(); } + auto begin() const { return m_map.begin(); } + auto end() const { return m_map.end(); } + +private: + std::map m_map; +}; + +//注意事项: +// 1.必须使用halconEngineManager::instance().loadAlgoFile加载算法文件; +// 2.输入输出参数必须通过callback传入/传出,且必须存入map中,key为参数名,会使用此map校验参数名 +// 3.原生HTupleVector被舍弃,将使用std::vector代替 +template +class halconEvaluater { +public: +#pragma region define + using inputCallback = std::function& inputHObj, + std::map& inputHTuple, + const halconEvaluaterInputTemplate&)>;//将输入存入结构体中 + using outputCallback = std::function& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + halconEvaluaterOutputTemplate&)>;//将输出存入结构体中 + struct initResult { + bool result = true; + std::string failedReason; + }; + +#pragma endregion + + + halconEvaluater() = default; + ~halconEvaluater() { + if (_reloadCallbackId >= 0) + halconEngineManager::instance().unregisterReloadCallback(_reloadCallbackId); + } + initResult init(const std::string& algoNameIn, + inputCallback callbackIn, + outputCallback callbackOut) { + if (inited)return { false,"重复初始化" }; + // 1.初始化/获取所有参数 + algoName = algoNameIn; + _inputCallback = callbackIn; + _outputCallback = callbackOut; + + //获取输入参数列表 + std::map inputHObj; + std::map inputHTuple; + halconEvaluaterInputTemplate input; + callbackIn(inputHObj, inputHTuple, input); + for (auto& kv : inputHObj) inputHObjNames.push_back(kv.first); + for (auto& kv : inputHTuple) inputHTupleNames.push_back(kv.first); + + //获取输出参数列表 + std::map outputHObj; + std::map outputHTuple; + std::map> outputHTupleVec; + halconEvaluaterOutputTemplate output; + callbackOut(outputHObj, outputHTuple, outputHTupleVec, output); + for (auto& kv : outputHObj) outputHObjNames.push_back(kv.first); + for (auto& kv : outputHTuple) outputHTupleNames.push_back(kv.first); + for (auto& kv : outputHTupleVec) outputHTupleVectorNames.push_back(kv.first); + //初始化通过 + inited = true; + + //如果halconEngineManager已初始化,立即自检 + //否则等halconEngineManager初始化时通过回调自检 + if (halconEngineManager::instance().isInited()) { + auto selfCheckResult = onSelfCheck(); + if (!selfCheckResult.first) { + return { selfCheckResult.first,selfCheckResult.second }; + } + } + + // 注册reload回调:文件重载时重新自检 + _reloadCallbackId = halconEngineManager::instance().registerReloadCallback([this]() -> std::pair { + inited = false; + auto result = onSelfCheck(); + if (result.first) { + inited = true; + } + else { + std::cout << algoName << "未通过自检,请检查后重试"; + qDebug() << QString::fromStdString(algoName) << "未通过自检,请检查后重试"; + auto bridge = halconEngineManager::instance().getSignalHub(); + if (bridge != nullptr) { + emit bridge->showMessageBox(QString::fromStdString(algoName) + "算法未通过自检,请检查后\n错误信息:" + QString::fromStdString(result.second)); + } + } + return result; + }); + + halconEngineManager::instance().registerTimeCostViewer(QString::fromStdString(algoName)); + return { true,"" }; + } + + struct halconResult { + bool runFlag = false;//运行是否成功 + std::string faildResult;//运行失败原因 + halconEvaluaterOutputTemplate result;//运行结果 + double costTime = 0;//算法耗时 + }; + halconResult evaluate(const halconEvaluaterInputTemplate& input) { + if (!inited) { + std::string reason = "函数未初始化或重载后自检失败"; + qWarning() << QString::fromStdString(reason); + std::cout << "[evaluate] " << reason << std::endl; + return { false,reason, halconEvaluaterOutputTemplate(),-1 }; + } + + try { +#pragma region 通过inputCallback将input结构体数据写入halcon参数map + std::map inputHObj; + std::map inputHTuple; + _inputCallback(inputHObj, inputHTuple, input); +#pragma endregion + +#pragma region 设置输入参数并计时执行 + HDevEngineCpp::HDevProcedure proc((char*)algoName.c_str()); + HDevEngineCpp::HDevProcedureCall procedureCall(proc); + + for (const auto& kv : inputHObj) + procedureCall.SetInputIconicParamObject(kv.first.c_str(), kv.second); + for (const auto& kv : inputHTuple) + procedureCall.SetInputCtrlParamTuple(kv.first.c_str(), kv.second); + + auto t0 = std::chrono::steady_clock::now(); + procedureCall.Execute(); + auto t1 = std::chrono::steady_clock::now(); + double elapsed = std::chrono::duration(t1 - t0).count(); + halconEngineManager::instance().submitTime(algoName, elapsed); +#pragma endregion + +#pragma region 获取输出参数,通过outputCallback填入output结构体 + std::map outputHObj; + std::map outputHTuple; + std::map> outputHTupleVec; + for (const auto& name : outputHObjNames) + outputHObj[name] = procedureCall.GetOutputIconicParamObject(name.c_str()); + for (const auto& name : outputHTupleNames) + outputHTuple[name] = procedureCall.GetOutputCtrlParamTuple(name.c_str()); + for (const auto& name : outputHTupleVectorNames) { + HalconCpp::HTupleVector vec = procedureCall.GetOutputCtrlParamVector(name.c_str()); + for (int i = 0; i < vec.Length(); i++) { + outputHTupleVec[name].push_back(vec[i].T()); + } + } + + halconEvaluaterOutputTemplate output; + _outputCallback(outputHObj, outputHTuple, outputHTupleVec, output); +#pragma endregion + + return { true,"", output ,elapsed }; + } + catch (HalconCpp::HException& e) { + std::string reason = "Halcon异常: " + std::string(e.ErrorMessage().Text()); + qWarning() << QString::fromStdString(reason); + std::cout << "[evaluate] " << reason << std::endl; + return { false, reason, halconEvaluaterOutputTemplate() ,-1 }; + } + } + +private: + std::atomic inited = false; + int _reloadCallbackId = -1; + std::string algoName; + inputCallback _inputCallback; + outputCallback _outputCallback; + std::vector inputHObjNames, inputHTupleNames, inputHTupleVectorNames; + std::vector outputHObjNames, outputHTupleNames, outputHTupleVectorNames; + + //front->result second->failedReason + //检查函数名是否存在,输入输出参数是否匹配 + std::pair onSelfCheck() { + // 1.检查函数名是否存在 + auto checkAlgoNameResult = checkAlgoName(algoName); + if (!checkAlgoNameResult.first) { + return { false, checkAlgoNameResult.second }; + } + // 2.检查输入输出参数是否匹配 + HDevEngineCpp::HDevProcedure proc((char*)algoName.c_str()); + auto checkInputResult = checkInput(proc, inputHObjNames, inputHTupleNames, inputHTupleVectorNames); + if (!checkInputResult.first) { + return { false, checkInputResult.second }; + } + auto checkOutputResult = checkOuput(proc, outputHObjNames, outputHTupleNames, outputHTupleVectorNames); + if (!checkOutputResult.first) { + return { false, checkOutputResult.second }; + } + qDebug() << QString::fromStdString(algoName) << "已通过自检"; + std::cout << algoName << "已通过自检" << std::endl; + return { true,"" }; + } + + std::pair checkAlgoName(const std::string& algoName) { + HDevEngineCpp::HDevEngine myEngine; + HalconCpp::HTuple hAlgoNames = myEngine.GetLoadedProcedureNames(); + for (int i = 0; i < hAlgoNames.Length(); i++) { + if (std::string(hAlgoNames[i].S().Text()) == algoName) + return { true,"" }; + } + + std::string reason = "输入的函数名与加载的函数名不匹配,输入的为" + algoName; + qWarning() << QString::fromStdString(reason); + std::cout << "[checkAlgoName] " << reason << std::endl; + return { false, reason }; + } + + std::pair checkInput( + const HDevEngineCpp::HDevProcedure& proc, + const std::vector& inputHObjectList, + const std::vector& inputHTupleList, + const std::vector& inputHTupleVectorList){ + + std::vector hobjList(inputHObjectList); + // HTuple 和 HTupleVector 均通过 ctrl 参数传递,合并校验 + std::vector htupleList(inputHTupleList); + for (const auto& n : inputHTupleVectorList) htupleList.push_back(n); + + std::vector> unPairedNames; + + + HTuple hInputHObjectNames = proc.GetInputIconicParamNames(); + for (int i = 0; i < hInputHObjectNames.Length(); i++) { + std::string name = hInputHObjectNames[i].S().Text(); + auto it = std::find(hobjList.begin(), hobjList.end(), name); + if (it != hobjList.end()) { + hobjList.erase(it); + } + else { + unPairedNames.emplace_back("HObject", name); + } + } + + HTuple hInputHTupleNames = proc.GetInputCtrlParamNames(); + for (int i = 0; i < hInputHTupleNames.Length(); i++) { + std::string name = hInputHTupleNames[i].S().Text(); + auto it = std::find(htupleList.begin(), htupleList.end(), name); + if (it != htupleList.end()) { + htupleList.erase(it); + } + else { + unPairedNames.emplace_back("HTuple/HTupleVector", name); + } + } + + if (!unPairedNames.empty() || !hobjList.empty() || !htupleList.empty()) { + + std::string failedReason = "输入参数不匹配! 未输入的外部参数"; + + if (hobjList.size() > 0) { + failedReason += " HObject{"; + for (const auto& name : hobjList) + failedReason += name + " "; + + failedReason += "}"; + } + + if (htupleList.size() > 0) { + failedReason += " HTuple/HTupleVector{"; + for (const auto& name : htupleList) + failedReason += name + " "; + failedReason += "}"; + } + + if (unPairedNames.size() > 0) { + failedReason += "HALCON内部缺失或未匹配{"; + for (const auto& p : unPairedNames) + failedReason += p.first + ":" + p.second + " "; + failedReason += "}"; + } + + qWarning() << QString::fromStdString(failedReason); + std::cout << "[checkInput] " << failedReason << std::endl; + return { false, failedReason }; + } + + return { true, "" }; + } + + std::pair checkOuput( + const HDevEngineCpp::HDevProcedure& proc, + const std::vector& outputHObjectList, + const std::vector& outputHTupleList, + const std::vector& outputHTupleVectorList){ + + std::vector hobjList(outputHObjectList); + // HTuple 和 HTupleVector 均通过 ctrl 参数传递,合并校验 + std::vector htupleList(outputHTupleList); + for (const auto& n : outputHTupleVectorList) htupleList.push_back(n); + + std::vector> unPairedNames; + HTuple hOutputHObjectNames = proc.GetOutputIconicParamNames(); + for (int i = 0; i < hOutputHObjectNames.Length(); i++) { + std::string name = hOutputHObjectNames[i].S().Text(); + auto it = std::find(hobjList.begin(), hobjList.end(), name); + if (it != hobjList.end()) { + hobjList.erase(it); + } + else { + unPairedNames.emplace_back("HObject", name); + } + } + + HTuple hOutputHTupleNames = proc.GetOutputCtrlParamNames(); + for (int i = 0; i < hOutputHTupleNames.Length(); i++) { + std::string name = hOutputHTupleNames[i].S().Text(); + auto it = std::find(htupleList.begin(), htupleList.end(), name); + if (it != htupleList.end()) { + htupleList.erase(it); + } + else { + unPairedNames.emplace_back("HTuple/HTupleVector", name); + } + } + + if (!hobjList.empty() || !htupleList.empty()) { + std::string failedReason = algoName + "输出参数不匹配! 外部多余参数"; + + if (hobjList.size() > 0) { + failedReason += " HObject{"; + for (const auto& name : hobjList) + failedReason += name + " "; + + failedReason += "}"; + } + + if (htupleList.size() > 0) { + failedReason += " HTuple/HTupleVector{"; + for (const auto& name : htupleList) + failedReason += name + " "; + failedReason += "}"; + } + + failedReason += "HALCON内部未匹配{"; + + for (const auto& p : unPairedNames) + failedReason += p.first + ":" + p.second + " "; + + failedReason += "}"; + + qWarning() << QString::fromStdString(failedReason); + std::cout << "[checkOuput] " << failedReason << std::endl; + return { false, failedReason }; + } + + if (!unPairedNames.empty()) { + std::string failedReason = algoName + "存在内部输出但外部未取用的参数! 多余的输出参数 {"; + + for (const auto& p : unPairedNames) + failedReason += p.first + ":" + p.second + " "; + + failedReason += "},不影响检测已通过自检!"; + + qWarning() << QString::fromStdString(failedReason); + std::cout << "[checkOuput] " << failedReason << std::endl; + return { true, failedReason }; + } + return { true, "" }; + } + +}; + +//USAGE +//class baoMoHouDiBuDetParams { +//public: +// struct input { +// HObject in_Image; +// HTuple in_Par; +// HTuple in_FilePath = "./halconFiles/"; +// HTuple in_Resolution; +// }; +// struct output { +// std::vector out_VectorX, out_VectorY; +// HTuple out_Flag, out_NgDetails; +// }; +// halconEvaluater evaluater; +// baoMoHouDiBuDetParams() { +// evaluater.init("_4_TD_BlueCover_Inspection", +// [](std::map& inputHObj, +// std::map& inputHTuple, +// const input& in) { +// inputHObj["in_Image"] = in.in_Image; +// inputHTuple["in_Par"] = in.in_Par; +// inputHTuple["in_FilePath"] = in.in_FilePath; +// inputHTuple["in_Resolution"] = in.in_Resolution; +// }, +// [](std::map& outputHObj, +// std::map& outputHTuple, +// std::map>& outputHTupleVector, +// output& out) { +// out.out_VectorX = outputHTupleVector["out_VectorX"]; +// out.out_VectorY = outputHTupleVector["out_VectorY"]; +// out.out_Flag = outputHTuple["out_Flag"]; +// out.out_NgDetails = outputHTuple["out_NgDetails"]; +// }); +// } +//}; \ No newline at end of file diff --git a/src/core/service/handleAiGpuThreadGuard.h b/src/core/service/handleAiGpuThreadGuard.h new file mode 100644 index 0000000..ded980f --- /dev/null +++ b/src/core/service/handleAiGpuThreadGuard.h @@ -0,0 +1,629 @@ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +class handleAiModelInfo { +public: + handleAiModelInfo(bSignalHub* bridgeIn) :bridge(bridgeIn) {}; + ~handleAiModelInfo() = default; + struct getModelPtrResult { + handleAiModelPtr* modelPtr = nullptr; + int gpuId = -1; + QMutex* modelMutex = nullptr; + }; + int getModelCount() { + QMutexLocker locker(&vectorMutex); + return static_cast(modelPtrs.size()); + } + getModelPtrResult getModelPtr(const int& id) { + QMutexLocker locker(&vectorMutex); + if (id < 0 || id >= static_cast(modelPtrs.size()) || id >= static_cast(modelMutexs.size())) { + qCritical() << QString::fromStdString(path) << "模型ID越界:" << id; + return{}; + } + return{ modelPtrs[id].second, modelPtrs[id].first, modelMutexs[id]}; + } + int addModelPtr(handleAiModelPtr* ptr,const int& gpuId,QString modelDiscribeIn) { + QMutexLocker locker(&vectorMutex); + modelDiscribe = modelDiscribeIn; + modelPtrs.push_back({ gpuId,ptr }); + modelMutexs.push_back(new QMutex()); + return static_cast(modelPtrs.size()) - 1; // 返回新添加模型的ID + } + void updateModelPtr(int id, handleAiModelPtr* newPtr) { + QMutexLocker locker(&vectorMutex); + if (id < 0 || id >= static_cast(modelPtrs.size())) { + qCritical() << QString::fromStdString(path) << "模型ID越界:" << id; + return; + } + modelPtrs[id].second = newPtr; + } + void submitTime(double time) { + QMutexLocker locker(&timeMutex); + totalTime += time; + callCount++; + maxCostTime = std::max(maxCostTime, time); + minCostTime = std::min(minCostTime, time); + //每十次发送一次平均耗时到界面 每千次输出一次日志并重置数据 + if (callCount % 10 == 0) { + double avgTime = callCount > 0 ? totalTime / callCount : 0.0; + emit bridge->handleAiManager_refreshTimeCostViewer(modelDiscribe, QString::number(avgTime, 'f', 2) + "ms", QString::number(maxCostTime, 'f', 2) + "ms", QString::number(minCostTime, 'f', 2) + "ms"); + if (callCount == 1000) { + qInfo() << QString::fromStdString(path) + "近千次平均耗时" << avgTime << "ms,最高" << maxCostTime << "ms,最低" << minCostTime << "ms"; + callCount = 0; + totalTime = 0; + maxCostTime = 0; + minCostTime = 99999.0; + } + } + } + double avgTime() { QMutexLocker locker(&timeMutex); return callCount > 0 ? totalTime / callCount : 0.0; }// 平均耗时(毫秒) + double maxTime() { QMutexLocker locker(&timeMutex); return maxCostTime; }// 最大耗时(毫秒) + double minTime() { QMutexLocker locker(&timeMutex); return minCostTime; }// 最小耗时(毫秒) + std::string path; +private: + QString modelDiscribe;//模型描述 + bSignalHub* bridge = nullptr; + QMutex vectorMutex; // 保护modelPtrs和modelMutexs的互斥锁 + int index = 0; + std::vector> modelPtrs; + std::vector modelMutexs; + + QMutex timeMutex; // 保护耗时统计的互斥锁 + double totalTime = 0.0, maxCostTime = 0.0, minCostTime = 99999.0; + int callCount = 0;// 提交耗时的次数 +}; + +class segModelWithGuard { +public: + segModelWithGuard(int idIn, std::shared_ptr> segPtrIn) { + segInfoPtr = segPtrIn; + auto modelPtrResult = segInfoPtr->getModelPtr(idIn); + segPtr = modelPtrResult.modelPtr; + modelMutex = modelPtrResult.modelMutex; + id = modelPtrResult.gpuId; + } + ~segModelWithGuard() = default; + + cv::Mat Evaluate(const cv::Mat& input) { + if (!segPtr || !segInfoPtr || !modelMutex) { + qCritical() << QString::fromStdString(segInfoPtr->path) << "模型未初始化"; + return cv::Mat(); + } + QMutexLocker locker(modelMutex); + timer.restart(); + cv::Mat result = segPtr->Evaluate(input); + segInfoPtr->submitTime(timer.elapsed()); + return result; + } + std::vector bunchEvaluate(const std::vector& input) { + std::vector result; + if (!segPtr || !segInfoPtr || !modelMutex) { + qCritical() << QString::fromStdString(segInfoPtr->path) << "模型未初始化"; + return result; + } + result.reserve(input.size()); + QMutexLocker locker(modelMutex); + for (const auto& img : input) { + result.push_back(segPtr->Evaluate(img)); + } + return result; + } +private: + QElapsedTimer timer; + int id = -1; + handdleAI::pSegmentation* segPtr = nullptr; + std::shared_ptr> segInfoPtr = nullptr; + QMutex* modelMutex = nullptr;//根据不同模型共用的锁 +}; + +class clsModelWithGuard { +public: + clsModelWithGuard(int idIn, std::shared_ptr> ptrIn) { + clsInfoPtr = ptrIn; + auto modelPtrResult = clsInfoPtr->getModelPtr(idIn); + clsModelPtr = modelPtrResult.modelPtr; + modelMutex = modelPtrResult.modelMutex; + id = modelPtrResult.gpuId; + } + ~clsModelWithGuard() = default; + struct clsResult { + int classes = -1; + double prob = 0.0; + }; + clsResult Evaluate(const cv::Mat& input) { + if (!clsModelPtr || !clsInfoPtr || !modelMutex) { + qCritical() << QString::fromStdString(clsInfoPtr ? clsInfoPtr->path : std::string()) << "模型未初始化"; + return { -1,0.0 }; + } + QMutexLocker locker(modelMutex); + timer.restart(); + clsResult result{ clsModelPtr->Evaluate(input),clsModelPtr->getProbability() }; + clsInfoPtr->submitTime(timer.elapsed()); + return result; + } + std::vector bunchEvaluate(const std::vector& input) { + std::vector result; + if (!clsModelPtr || !clsInfoPtr || !modelMutex) { + qCritical() << QString::fromStdString(clsInfoPtr ? clsInfoPtr->path : std::string()) << "模型未初始化"; + return result; + } + result.reserve(input.size()); + QMutexLocker locker(modelMutex); + for (const auto& img : input) { + result.push_back({ clsModelPtr->Evaluate(img),clsModelPtr->getProbability() }); + } + return result; + } +private: + QElapsedTimer timer; + int id = -1; + handdleAI::pClassification* clsModelPtr = nullptr; + std::shared_ptr> clsInfoPtr = nullptr; + QMutex* modelMutex = nullptr; +}; + +class detModelWithGuard { +public: + detModelWithGuard(int idIn, std::shared_ptr> ptrIn) { + detInfoPtr = ptrIn; + auto modelPtrResult = detInfoPtr->getModelPtr(idIn); + detModelPtr = modelPtrResult.modelPtr; + modelMutex = modelPtrResult.modelMutex; + id = modelPtrResult.gpuId; + } + ~detModelWithGuard() = default; + + // 单张推理 + auto Evaluate(const cv::Mat& input) { + if (!detModelPtr || !detInfoPtr || !modelMutex) { + qCritical() << QString::fromStdString(detInfoPtr ? detInfoPtr->path : std::string()) << "模型未初始化"; + return decltype(detModelPtr->Evaluate(input))(); + } + + QMutexLocker locker(modelMutex); + timer.restart(); + auto result = detModelPtr->Evaluate(input); + detInfoPtr->submitTime(timer.elapsed()); + return result; + } + + // 批量推理 + std::vector>bunchEvaluate(const std::vector& input) { + + std::vector> result; + + if (!detModelPtr || !detInfoPtr || !modelMutex) { + qCritical() << QString::fromStdString(detInfoPtr ? detInfoPtr->path : std::string()) << "模型未初始化"; + return result; + } + + QMutexLocker locker(modelMutex); + + result.reserve(input.size()); + for (const auto& img : input) { + result.push_back(detModelPtr->Evaluate(img)); + } + + return result; + } + +private: + QElapsedTimer timer; + int id = -1; + handdleAI::pDetection* detModelPtr = nullptr; + std::shared_ptr> detInfoPtr = nullptr; + QMutex* modelMutex = nullptr; +}; + +class handleAiManager { +public: + handleAiManager(bSignalHub* bridgeIn) :bridge(bridgeIn) { + }; + ~handleAiManager() { + QMutexLocker managerLocker(&mutex); + // 析构时清除所有模型实例与互斥锁 + for (auto& info : segPtrs) { + int modelCount = info->getModelCount(); + for (int modelId = 0; modelId < modelCount; ++modelId) { + auto modelPtrResult = info->getModelPtr(modelId); + if (modelPtrResult.modelMutex) { + QMutexLocker modelLocker(modelPtrResult.modelMutex); + if (modelPtrResult.modelPtr) { + handdleAI::DestroySegObj(modelPtrResult.modelPtr); + } + } + else if (modelPtrResult.modelPtr) { + handdleAI::DestroySegObj(modelPtrResult.modelPtr); + } + delete modelPtrResult.modelMutex; + } + } + + + for (auto& info : clsPtrs) { + int modelCount = info->getModelCount(); + for (int modelId = 0; modelId < modelCount; ++modelId) { + auto modelPtrResult = info->getModelPtr(modelId); + if (modelPtrResult.modelMutex) { + QMutexLocker modelLocker(modelPtrResult.modelMutex); + if (modelPtrResult.modelPtr) { + handdleAI::DestroyClsObj(modelPtrResult.modelPtr); + } + } + else if (modelPtrResult.modelPtr) { + handdleAI::DestroyClsObj(modelPtrResult.modelPtr); + } + delete modelPtrResult.modelMutex; + } + } + + + for (auto& info : detPtrs) { + int modelCount = info->getModelCount(); + for (int modelId = 0; modelId < modelCount; ++modelId) { + auto modelPtrResult = info->getModelPtr(modelId); + if (modelPtrResult.modelMutex) { + QMutexLocker modelLocker(modelPtrResult.modelMutex); + if (modelPtrResult.modelPtr) { + handdleAI::DestroyDetObj(modelPtrResult.modelPtr); + } + } + else if (modelPtrResult.modelPtr) { + handdleAI::DestroyDetObj(modelPtrResult.modelPtr); + } + delete modelPtrResult.modelMutex; + } + } + + }; + //加载Seg模型 如未初始化会进行初始化 + //模型路径 批处理大小(batchSize) gpu序号 模型描述 + std::shared_ptr loadSegModel( + const std::string& path, + int batchSize, + int gpuIdIn, + QString modelDiscribe) { + + QMutexLocker locker(&mutex); + + //如果不存在则创建新的 + bool firtTime = false; + if (!segPtrs.contains(path)) { + segPtrs[path] = std::make_shared>(bridge); + segPtrs[path]->path = path; + firtTime = true; + } +#pragma region 检查文件是否存在,如果不存在则阻塞并等待下一步操作 + if (!std::filesystem::exists(path)) { + if (bridge != nullptr) { + int retryTimes = 0; + std::shared_ptr> recivedResult = std::make_shared>(false); + std::shared_ptr> result = std::make_shared>(-1); + emit bridge->waitForFrontendInitComfirm("找不到" + QString::fromStdString(path) + "请检查后点击重试!", { "重试","跳过" }, recivedResult, result); + while (1) { + while (recivedResult->load() == false) + QThread::msleep(100); + recivedResult->store(false); + if (result->load() == 0) {//重试 + if (std::filesystem::exists(path)) + break; + else { + emit bridge->waitForFrontendInitComfirm("依旧找不到" + QString::fromStdString(path) + "请检查后点击重试!重试次数" + QString::number(retryTimes++), { "重试","跳过" }, recivedResult, result); + } + } + else {//取消 + qWarning() << "[handleAiManager] model path does not exist:" << QString::fromStdString(path); + return nullptr; + } + } + + } + else { + qWarning() << "[handleAiManager] model path does not exist:" << QString::fromStdString(path); + return nullptr; + } + } +#pragma endregion + //获取详情 + auto segInfo = segPtrs[path]; + int modelId = segInfo->addModelPtr( + handdleAI::CreateSegObjS((char*)path.c_str(), batchSize, gpuIdIn), + gpuIdIn, modelDiscribe); + + //初次时注册在线更新按钮 + if (firtTime) { + emit bridge->modelInit_addBtn(modelDiscribe + " 模型在线更新", + "描述:" + modelDiscribe + + "\n路径:" + QString::fromStdString(path) + + + "\nbatchSize:" + QString::number(batchSize), + { "modelManager#update#seg#" + QString::fromStdString(path) + "#" + QString::number(batchSize) + "#" }); + emit bridge->handleAiManager_addTimeCostViewer(modelDiscribe, { "平均耗时","最大耗时","最小耗时" }); + } + qDebug() << modelDiscribe << "-" << QString::fromStdString(path) << " 加载成功"; + return std::make_shared(modelId, segInfo); + } + + //加载Cls模型 如已初始化则返回指针 + //模型路径 批处理大小(batchSize) gpu序号 模型描述 + std::shared_ptr loadClsModel( + const std::string& path, + int batchSize, + int gpuIdIn, + QString modelDiscribe) { + + QMutexLocker locker(&mutex); + //如果不存在则创建新的 + bool firtTime = false; + if (!clsPtrs.contains(path)) { + clsPtrs[path] = std::make_shared>(bridge); + clsPtrs[path]->path = path; + firtTime = true; + } + +#pragma region 检查文件是否存在,如果不存在则阻塞并等待下一步操作 + if (!std::filesystem::exists(path)) { + if (bridge != nullptr) { + int retryTimes = 0; + std::shared_ptr> recivedResult = std::make_shared>(false); + std::shared_ptr> result = std::make_shared>(-1); + emit bridge->waitForFrontendInitComfirm("找不到" + QString::fromStdString(path) + "请检查后点击重试!", { "重试","跳过" }, recivedResult, result); + while (1) { + while (recivedResult->load() == false) + QThread::msleep(100); + recivedResult->store(false); + if (result->load() == 0) {//重试 + if (std::filesystem::exists(path)) + break; + else { + emit bridge->waitForFrontendInitComfirm("依旧找不到" + QString::fromStdString(path) + "请检查后点击重试!重试次数" + QString::number(retryTimes++), { "重试","跳过" }, recivedResult, result); + } + } + else {//取消 + qWarning() << "[handleAiManager] model path does not exist:" << QString::fromStdString(path); + return nullptr; + } + } + + } + else { + qWarning() << "[handleAiManager] model path does not exist:" << QString::fromStdString(path); + return nullptr; + } + } +#pragma endregion + //获取详情 + auto clsInfo = clsPtrs[path]; + int modelId = clsInfo->addModelPtr( + handdleAI::CreateClsObjV3((char*)path.c_str(), gpuIdIn, batchSize), + gpuIdIn, modelDiscribe); + + ////初次时注册在线更新按钮 + if (firtTime) { + emit bridge->modelInit_addBtn(modelDiscribe + " 模型在线更新", + "描述:" + modelDiscribe + + "\n路径:" + QString::fromStdString(path) + + "\nbatchSize:" + QString::number(batchSize), + { "modelManager#update#cls#" + QString::fromStdString(path) + "#" + QString::number(batchSize) + "#" }); + emit bridge->handleAiManager_addTimeCostViewer(modelDiscribe, { "平均耗时","最大耗时","最小耗时" }); + } + qDebug() << modelDiscribe << "-" << QString::fromStdString(path) << " 加载成功"; + return std::make_shared(modelId, clsInfo); + } + + //加载Det模型 如已初始化则返回指针 + //模型路径 批处理大小(batchSize) gpu序号 模型描述 + std::shared_ptr loadDetModel( + const std::string& path, + int batchSize, + int gpuIdIn, + QString modelDiscribe) { + + QMutexLocker locker(&mutex); + //如果不存在则创建新的 + bool firtTime = false; + if (!detPtrs.contains(path)) { + detPtrs[path] = std::make_shared>(bridge); + detPtrs[path]->path = path; + firtTime = true; + } +#pragma region 检查文件是否存在,如果不存在则阻塞并等待下一步操作 + if (!std::filesystem::exists(path)) { + if (bridge != nullptr) { + int retryTimes = 0; + std::shared_ptr> recivedResult = std::make_shared>(false); + std::shared_ptr> result = std::make_shared>(-1); + emit bridge->waitForFrontendInitComfirm("找不到" + QString::fromStdString(path) + "请检查后点击重试!", { "重试","跳过" }, recivedResult, result); + while (1) { + while (recivedResult->load() == false) + QThread::msleep(100); + recivedResult->store(false); + if (result->load() == 0) {//重试 + if (std::filesystem::exists(path)) + break; + else { + emit bridge->waitForFrontendInitComfirm("依旧找不到" + QString::fromStdString(path) + "请检查后点击重试!重试次数" + QString::number(retryTimes++), { "重试","跳过" }, recivedResult, result); + } + } + else {//取消 + qWarning() << "[handleAiManager] model path does not exist:" << QString::fromStdString(path); + return nullptr; + } + } + + } + else { + qWarning() << "[handleAiManager] model path does not exist:" << QString::fromStdString(path); + return nullptr; + } + } +#pragma endregion + //获取详情 + auto detInfo = detPtrs[path]; + int modelId = detInfo->addModelPtr( + handdleAI::CreateDetObjS((char*)path.c_str(), batchSize, gpuIdIn), + gpuIdIn, modelDiscribe); + + ////初次时注册在线更新按钮 + if (firtTime) { + emit bridge->modelInit_addBtn(modelDiscribe + " 模型在线更新", + "描述:" + modelDiscribe + + "\n路径:" + QString::fromStdString(path) + + + "\nbatchSize:" + QString::number(batchSize), + { "modelManager#update#det#" + QString::fromStdString(path) + "#" + QString::number(batchSize) + "#" }); + emit bridge->handleAiManager_addTimeCostViewer(modelDiscribe, { "平均耗时","最大耗时","最小耗时" }); + } + qDebug() << modelDiscribe << "-" << QString::fromStdString(path) << " 加载成功"; + return std::make_shared(modelId, detInfo); + } + + //热更新现有模型 + void updateSegModel( + const std::string& oldModelPath, + const std::string& newModelPath, + int batchSize) { + updateModel(oldModelPath, newModelPath, batchSize, seg); + } + + //热更新现有模型 + void updateClsModel( + const std::string& oldModelPath, + const std::string& newModelPath, + int batchSize) { + updateModel(oldModelPath, newModelPath, batchSize, cls); + } + + //热更新现有模型 + void updateDetModel( + const std::string& oldModelPath, + const std::string& newModelPath, + int batchSize) { + updateModel(oldModelPath, newModelPath, batchSize, det); + } + + +private: + QString modelBackupPath = "./handleAiModels/backup/"; + enum modelType{ seg,cls,det }; + + void updateModel( + const std::string& oldModelPath, + const std::string& newModelPath, + int batchSize, + modelType type) { + + QMutexLocker managerLocker(&mutex); + bool updated = false; + //检查模型是否存在 + bool exist = segPtrs.contains(oldModelPath); + if (!exist) { + emit bridge->modelInit_modelInitResult(false, QString::fromStdString(oldModelPath) + "模型不存在"); + return; + } + + //先进行模型备份及文件移动 + QFileInfo info(QString::fromStdString(oldModelPath)); + QString backupPath = modelBackupPath + info.fileName() + QDateTime::currentDateTime().toString("yyyy_MM_dd_hh-mm-ss"); + createDirectoryIfNotExists(backupPath); + //移动原BHD文件 + QFile(QString::fromStdString(oldModelPath)).rename(backupPath + "/" + info.fileName()); + QString oldModelDirPath = info.absoluteDir().absolutePath(); + auto fileInfoList = QDir(info.absoluteDir()).entryInfoList(); + //移动原初始化文件 + for (auto& fileInfo : fileInfoList) { + if (fileInfo.baseName() == info.baseName() && !fileInfo.fileName().contains("BHD")) { + QFile(fileInfo.absoluteFilePath()).rename(backupPath + "/" + fileInfo.fileName()); + } + } + //移动新的模型文件 + QFile(QString::fromStdString(newModelPath)).rename(QString::fromStdString(oldModelPath)); + //依次初始化 + switch (type) { + case seg: + if (segPtrs.contains(oldModelPath)) { + auto& segInfo = segPtrs[oldModelPath]; + int modelCount = segInfo->getModelCount(); + for (int modelId = 0; modelId < modelCount; ++modelId) { + auto oldModelPtr = segInfo->getModelPtr(modelId); + if (!oldModelPtr.modelPtr || !oldModelPtr.modelMutex) { + emit bridge->modelInit_modelInitResult(false, QString::fromStdString(oldModelPath) + "模型实例无效"); + continue; + } + handdleAI::pSegmentation* newPtr = handdleAI::CreateSegObjS((char*)oldModelPath.c_str(), batchSize, oldModelPtr.gpuId); + { + QMutexLocker modelLocker(oldModelPtr.modelMutex); + segInfo->updateModelPtr(modelId, newPtr); + } + handdleAI::DestroySegObj(oldModelPtr.modelPtr); + updated = true; + } + } + break; + case cls: + if (clsPtrs.contains(oldModelPath)) { + auto& clsInfo = clsPtrs[oldModelPath]; + int modelCount = clsInfo->getModelCount(); + for (int modelId = 0; modelId < modelCount; ++modelId) { + auto oldModelPtr = clsInfo->getModelPtr(modelId); + if (!oldModelPtr.modelPtr || !oldModelPtr.modelMutex) { + emit bridge->modelInit_modelInitResult(false, QString::fromStdString(oldModelPath) + "模型实例无效"); + continue; + } + handdleAI::pClassification* newPtr = handdleAI::CreateClsObjV3((char*)oldModelPath.c_str(), oldModelPtr.gpuId, batchSize); + { + QMutexLocker modelLocker(oldModelPtr.modelMutex); + clsInfo->updateModelPtr(modelId, newPtr); + } + handdleAI::DestroyClsObj(oldModelPtr.modelPtr); + updated = true; + } + } + break; + case det: + if (detPtrs.contains(oldModelPath)) { + auto& detInfo = detPtrs[oldModelPath]; + int modelCount = detInfo->getModelCount(); + for (int modelId = 0; modelId < modelCount; ++modelId) { + auto oldModelPtr = detInfo->getModelPtr(modelId); + if (!oldModelPtr.modelPtr || !oldModelPtr.modelMutex) { + emit bridge->modelInit_modelInitResult(false, QString::fromStdString(oldModelPath) + "模型实例无效"); + continue; + } + handdleAI::pDetection* newPtr = handdleAI::CreateDetObjS((char*)oldModelPath.c_str(), batchSize, oldModelPtr.gpuId); + { + QMutexLocker modelLocker(oldModelPtr.modelMutex); + detInfo->updateModelPtr(modelId, newPtr); + } + handdleAI::DestroyDetObj(oldModelPtr.modelPtr); + updated = true; + } + } + break; + } + if (!updated) { + emit bridge->modelInit_modelInitResult(false, QString::fromStdString(oldModelPath) + "路径不存在,无法更新模型"); + } + else { + emit bridge->modelInit_modelInitResult(true, QString::fromStdString(oldModelPath) + "成功"); + } + } + bSignalHub* bridge; + handleAiManager(const handleAiManager&) = delete; + handleAiManager& operator=(const handleAiManager&) = delete; + + //key为路径 唯一 + QMap>>segPtrs; + QMap>>clsPtrs; + QMap>>detPtrs; + QMutex mutex; +}; diff --git a/src/core/service/imgSaveThreadManager.h b/src/core/service/imgSaveThreadManager.h index 2e7f1af..594f381 100644 --- a/src/core/service/imgSaveThreadManager.h +++ b/src/core/service/imgSaveThreadManager.h @@ -12,6 +12,229 @@ #include #include #include +#include +#include +struct memUsage { + DWORDLONG totalPhys = 0;//总物理内存大小 + DWORDLONG availPhys = 0;//可用物理内存大小 + DWORDLONG usedPhys = 0;//已使用物理内存大小 + + DWORDLONG totalVir = 0;//总虚拟内存大小 + DWORDLONG availVir = 0;//可用虚拟内存大小 + DWORDLONG usedVir = 0;//已使用虚拟内存大小 + + DWORDLONG totalMem = 0;//总物理+虚拟内存大小 + DWORDLONG availMem = 0;//可用物理+虚拟内存大小 + DWORDLONG usedMem = 0;//已使用物理+虚拟内存大小 + + double usedPhysPercent = 0.0;//已使用物理内存大小的百分比 + double usedMemPercent = 0.0;//已使用物理+虚拟内存大小的百分比 +}; +static memUsage getMemUsage() { + memUsage result; + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(memInfo); + + if (GlobalMemoryStatusEx(&memInfo)) + { + result.totalPhys = memInfo.ullTotalPhys; + result.availPhys = memInfo.ullAvailPhys; + result.usedPhys = result.totalPhys - result.availPhys; + + result.totalVir = memInfo.ullTotalPageFile - memInfo.ullTotalPhys; + result.availVir = memInfo.ullAvailPageFile - memInfo.ullAvailPhys; + result.usedVir = result.totalVir - result.availVir; + + result.totalMem = memInfo.ullTotalPageFile; + result.availMem = memInfo.ullAvailPageFile; + result.usedMem = result.totalMem - result.availMem; + + result.usedPhysPercent = ((double)result.usedPhys / (double)result.totalPhys) * 100; + result.usedMemPercent = ((double)result.usedMem / (double)result.totalMem) * 100; + } + return result; +} + +//用于缓存需要创建的文件夹路径,防止重复创建 +class LRUCache { +public: + explicit LRUCache(int capacity) : maxCapacity(capacity) { + if (capacity <= 0) { + throw std::invalid_argument("LRU缓存容量必须大于0"); + } + } + + // 检查字符串是否在缓存中 + bool contains(const QString& value) { + QMutexLocker locker(&mutex); + int index = cacheList.indexOf(value); + if (index != -1) { + // 移动到列表头部(最近使用) + cacheList.move(index, 0); + return true; + } + return false; + } + + // 添加字符串到缓存 + void put(const QString& value) { + QMutexLocker locker(&mutex); + if (value.isEmpty()) { + return; // 不缓存空字符串 + } + + int index = cacheList.indexOf(value); + if (index != -1) { + // 已存在,移动到头部 + cacheList.move(index, 0); + return; + } + + // 不存在,需要添加 + if (cacheList.size() >= maxCapacity) { + // 缓存已满,移除最久未使用的元素(尾部) + cacheList.removeLast(); + } + + // 添加新元素到头部 + cacheList.prepend(value); + } + + // 移除指定元素 + bool remove(const QString& value) { + QMutexLocker locker(&mutex); + int index = cacheList.indexOf(value); + if (index != -1) { + cacheList.removeAt(index); + return true; + } + return false; + } + + // 获取缓存大小 + int size() { + QMutexLocker locker(&mutex); + return cacheList.size(); + } + + // 获取最大容量 + int capacity() { + QMutexLocker locker(&mutex); + return maxCapacity; + } + + // 检查是否为空 + bool isEmpty() { + QMutexLocker locker(&mutex); + return cacheList.isEmpty(); + } + + // 清空缓存 + void clear() { + QMutexLocker locker(&mutex); + cacheList.clear(); + } + + +private: + QMutex mutex; + int maxCapacity; + QStringList cacheList; // QStringList,头部是最近使用的,尾部是最久未使用的 +}; +//汇总NG结果,同一个SN码有NG则NG +class ngSummary { +public: + explicit ngSummary(int capacity) : maxCapacity(capacity) { + if (capacity <= 0) { + throw std::invalid_argument("ngSummary缓存容量必须大于0"); + } + } + ~ngSummary() { + waitTimeSec = 0;//析构时将等待时间设置为0,通知所有等待的线程结束等待 + int waitCount = 0; + while(waitingSaveJobCount != 0) { + QThread::msleep(100); + waitCount++; + if(waitCount > 100) { + break; + } + } + } + struct ngJudgement { + bool aiDetOK = true; + bool halconOK = true; + std::vectordefects; + }; + ngJudgement waitForResult(const QString& SN, + const bool& aiDetOk, + const bool& halconOK, + const std::vector& defects) { + + addResult(SN, aiDetOk, halconOK, defects); + waitingSaveJobCount++;//增加等待存储的任务数量 + for (int i = 0; i < (int)waitTimeSec; i++) { + QThread::msleep(1000); + } + waitingSaveJobCount--;//减少等待存储的任务数量 + return getResult(SN); + } + void setWaitTimeSec(double waitTimeSecIn) { + waitTimeSec = waitTimeSecIn; + } +private: + void appendUniqueDefects(std::vector& dst, const std::vector& src) { + for (const auto& defect : src) { + if (defect.empty()) { + continue; + } + if (std::find(dst.begin(), dst.end(), defect) == dst.end()) { + dst.push_back(defect); + } + } + } + + void addResult(const QString& SN, bool aiDetOk, bool halconOK, const std::vector& defects) { + QMutexLocker locker(&mutex); + ngJudgement _ngJudgement = { aiDetOk,halconOK,{} }; + appendUniqueDefects(_ngJudgement.defects, defects); + + int index = cacheList.indexOf(SN); + if (index != -1) { + // 已存在,移动到头部 + cacheList.move(index, 0); + //如果已存在的结果不为NG,则赋值当前的结果 + if (map[SN].aiDetOK != false) + map[SN].aiDetOK = _ngJudgement.aiDetOK; + if (map[SN].halconOK != false) + map[SN].halconOK = _ngJudgement.halconOK; + appendUniqueDefects(map[SN].defects, _ngJudgement.defects); + return; + } + + // 不存在,需要添加 + if (cacheList.size() >= maxCapacity) { + // 缓存已满,移除最久未使用的元素(尾部) + QString lastStr = cacheList.last(); + cacheList.removeLast(); + map.remove(lastStr); + } + + // 添加新元素到头部 + cacheList.prepend(SN); + map[SN] = _ngJudgement; + } + ngJudgement getResult(const QString& SN) { + QMutexLocker locker(&mutex); + return map[SN]; + } + QMap map; + QMutex mutex; + int maxCapacity; + QStringList cacheList; // QStringList,头部是最近使用的,尾部是最久未使用的 + std::atomic waitingSaveJobCount = 0;//等待存储的任务数量 + std::atomic waitTimeSec = 6.0;//等待时间,单位秒 +}; + class ImageSavePathFormatter { public: enum imgSaveThreadType { @@ -21,7 +244,16 @@ public: clsImg }; QString rootDir = ""; - QString genPath(std::shared_ptr data, const QString& typeStr, QString customPath = "", std::vector>customParams = std::vector>()){ + + //根据输入的参数生成单个存图路径 + QString genPath(const std::string& snCode, + const std::string& cameraKeyName, + const QString& saveObjectName, + const QString& typeStr, + const QString& customPath = "", + const ngSummary::ngJudgement& ngSummaryResult = {true,true}, + const std::vector>& customParams = {}) { + QString pathTemplate = !customPath.isEmpty() ? customPath : defaultPath; QString result = pathTemplate; @@ -37,27 +269,27 @@ public: // 替换检测结果占位符 std::string processResult; - if (!data->aiDetOK && !data->halconOK) processResult = "NG"; - else if (!data->aiDetOK) processResult = "aiNG"; - else if (!data->halconOK) processResult = "algoNG"; + if (!ngSummaryResult.aiDetOK && !ngSummaryResult.halconOK) processResult = "NG"; + else if (!ngSummaryResult.aiDetOK) processResult = "aiNG"; + else if (!ngSummaryResult.halconOK) processResult = "algoNG"; else processResult = "OK"; - for (int i = 0; i < data->resultSaveText.size(); i++) { - processResult.append("_" + data->resultSaveText[i]); + for (int i = 0; i < ngSummaryResult.defects.size(); i++) { + processResult.append("_" + ngSummaryResult.defects[i]); } result.replace("{processResult}", QString::fromStdString(repairWindowsFileName(processResult))); // 替换SN/设备信息占位符 - QString snCode = (data->SNcode.size() > 0 && data->SNcode.size() < 50) - ? QString::fromStdString(repairWindowsFileName(data->SNcode)) - : QString::fromStdString(repairWindowsFileName(data->cameraKeyName)); - result.replace("{SN}", snCode); - result.replace("{cameraName}", QString::fromStdString(data->cameraKeyName)); + QString snCodeQStr = (snCode.size() > 0 && snCode.size() < 50) + ? QString::fromStdString(repairWindowsFileName(snCode)) + : QString::fromStdString(repairWindowsFileName(cameraKeyName)); + result.replace("{SN}", snCodeQStr); + result.replace("{cameraName}", QString::fromStdString(cameraKeyName)); // 替换根目录占位符 result.replace("{imgSaveRootDir}", rootDir.isEmpty() ? "." : rootDir); // 替换根目录占位符 - result.replace("{saveObjectName}", data->saveObjectName.isEmpty() ? "" : data->saveObjectName); + result.replace("{saveObjectName}", saveObjectName.isEmpty() ? "" : saveObjectName); // 确保路径以/结尾 if (!result.endsWith("/") && !result.endsWith("\\")) { @@ -68,7 +300,15 @@ public: } return result; } - std::string genFileName(std::shared_ptr data, const QString& typeStr, int index = 0, QString customFileName = "", std::vector> customParams = std::vector>(), const std::string& format = "bmp") { + + //根据输入的参数生成单个文件名 + std::string genFileName(const std::shared_ptr& data, + const QString& typeStr, + int index = 0, + const QString& customFileName = "", + const std::vector>& customParams = {}, + const std::string& format = "bmp") { + QString fileTemplate = !customFileName.isEmpty() ? customFileName : defaultFileName; QString result = fileTemplate; @@ -97,9 +337,14 @@ public: return result.toLocal8Bit().toStdString() + "." + format; } - std::vector genFileNames(std::shared_ptr data, imgSaveThreadType type, const std::vector& imgs) { + + //根据输入的参数生成一组文件名 + std::vector genFileNames(const std::shared_ptr& data, + imgSaveThreadType type, + const std::vector& imgs) { + static const QMap typeMap{ - { oriImg, "ORI" }, + { oriImg, "ORI" }, { resImg, "RES" }, { segImg, "SEG" }, { clsImg, "CLS" } @@ -128,7 +373,11 @@ public: } return result; } - std::vector genClsFileNames(std::shared_ptr data, const std::vector>& imgs, QString customFileName = "") { + + //根据输入的参数生成一组CLS图用的文件名 + std::vector genClsFileNames(const std::shared_ptr& data, + const std::vector>& imgs, + const QString& customFileName = "") { std::vector result; for (int i = 0; i < imgs.size(); i++) { result.push_back(genFileName(data, "CLS_" + QString::fromStdString(imgs[i].first), i, defaultFileName, data->customImageSaveTemplateParams)); @@ -177,7 +426,7 @@ private: struct saveJob { saveJob(std::string jobNameIn) :jobName(jobNameIn) {} std::string jobName;// 任务名称 - QQueue> imgQueue;// 图片队列 + QQueue>>> imgQueue;// 图片队列 }; // 数据库存储线程类 @@ -198,6 +447,7 @@ public: break; } localQueue.swap(pathQueue); + pendingCount -= localQueue.size(); } // 批量处理 @@ -205,7 +455,7 @@ public: // 创建表(如果不存在) sqlDB.createTable(tableName, { - {"directory_path", "TEXT UNIQUE"} // 使用UNIQUE约束避免重复 + {"directory_path", "TEXT"} // 使用UNIQUE约束避免重复 }); while (!localQueue.isEmpty()) { @@ -296,9 +546,9 @@ public: priority }; void run() { - try { - while (true) { - QQueue> localQueue; + while (true) { + try { + QQueue>>> localQueue; std::shared_ptr job = nullptr; // 优先处理 priorityQueue { @@ -322,32 +572,31 @@ public: // 批量保存 while (!localQueue.isEmpty()) { auto data = std::move(localQueue.dequeue()); - cv::imwrite(data.first, data.second); - QMutexLocker locker(&threadBufferSizeMutex); - threadBufferSize -= data.second.total() * data.second.elemSize(); + write(data.first, data.second); + threadBufferSize -= data.second->size(); } #endif qDebug() << "saveJob end " << QString::fromStdString(job->jobName); } + catch (cv::Exception& e) { + qCritical() << "OpenCV异常: " << e.what(); + } + catch (std::exception& e) { + qCritical() << "标准异常: " << e.what(); + } + catch (...) { + qCritical() << "未知异常: 存图崩溃"; + } } - catch (cv::Exception& e) { - qCritical() << "OpenCV异常: " << e.what(); - } - catch (std::exception& e) { - qCritical() << "标准异常: " << e.what(); - } - catch (...) { - qCritical() << "未知异常: 存图崩溃"; - } + } void addJob(std::shared_ptr job, type type_ = normal) { - { - QMutexLocker locker(&threadBufferSizeMutex); - for (auto& img : job->imgQueue) { - threadBufferSize += img.second.total() * img.second.elemSize(); - } + + for (auto& img : job->imgQueue) { + threadBufferSize += img.second->size(); } + QMutexLocker locker(&queueMutex); switch (type_) { case normal: @@ -370,120 +619,62 @@ public: return size; } int getThreadBufferSize() { - QMutexLocker locker(&threadBufferSizeMutex); return threadBufferSize; } private: std::vector compressionParams{ cv::IMWRITE_PNG_COMPRESSION ,9 }; QQueue< std::shared_ptr>priorityQueue; QQueue< std::shared_ptr>defaultQueue; - int threadIndex = 0, threadBufferSize = 0, imgRemainCount = 0; - QMutex queueMutex, threadBufferSizeMutex; -}; + int threadIndex = 0;//线程ID + std::atomicthreadBufferSize = 0, imgRemainCount = 0;//缓存大小及图片计数 + QMutex queueMutex; -//用于缓存需要创建的文件夹路径,防止重复创建 -class LRUCache { -public: - explicit LRUCache(int capacity) : maxCapacity(capacity) { - if (capacity <= 0) { - throw std::invalid_argument("LRU缓存容量必须大于0"); - } - } + bool write(const std::string& path, const std::shared_ptr> buffer) { + try { + std::ofstream ofs(path, std::ios::binary); + if (!ofs.is_open()) return false; - // 检查字符串是否在缓存中 - bool contains(const QString& value) { - QMutexLocker locker(&mutex); - int index = cacheList.indexOf(value); - if (index != -1) { - // 移动到列表头部(最近使用) - cacheList.move(index, 0); + ofs.write(reinterpret_cast(buffer->data()), buffer->size()); return true; } - return false; - } - - // 添加字符串到缓存 - void put(const QString& value) { - QMutexLocker locker(&mutex); - if (value.isEmpty()) { - return; // 不缓存空字符串 + catch (...) { + return false; } - - int index = cacheList.indexOf(value); - if (index != -1) { - // 已存在,移动到头部 - cacheList.move(index, 0); - return; - } - - // 不存在,需要添加 - if (cacheList.size() >= maxCapacity) { - // 缓存已满,移除最久未使用的元素(尾部) - cacheList.removeLast(); - } - - // 添加新元素到头部 - cacheList.prepend(value); } - - // 移除指定元素 - bool remove(const QString& value) { - QMutexLocker locker(&mutex); - int index = cacheList.indexOf(value); - if (index != -1) { - cacheList.removeAt(index); - return true; - } - return false; - } - - // 获取缓存大小 - int size() const { - return cacheList.size(); - } - - // 获取最大容量 - int capacity() const { - return maxCapacity; - } - - // 检查是否为空 - bool isEmpty() const { - return cacheList.isEmpty(); - } - - // 清空缓存 - void clear() { - QMutexLocker locker(&mutex); - cacheList.clear(); - } - - -private: - QMutex mutex; - int maxCapacity; - QStringList cacheList; // QStringList,头部是最近使用的,尾部是最久未使用的 }; class imgSaveThreadManager { public: - imgSaveThreadManager(std::string pathIn, paramSyncManager* paramSyncManagerIn) - :dbThread(std::make_unique("./sqlDatabase/imgSavePath.db")){ - _ImageSavePathFormatter.rootDir = QString::fromStdString(pathIn); - _paramSyncManager = paramSyncManagerIn; - saveExtraNgImg = _paramSyncManager->get("通用设置", "额外存储NG图片", false).toBool(); - onlySaveNgOri = _paramSyncManager->get("通用设置", "仅在NG时保存原图", false).toBool(); - onlySaveNgSeg = _paramSyncManager->get("通用设置", "仅在NG时保存Seg图", false).toBool(); - onlySaveNgCls = _paramSyncManager->get("通用设置", "仅在NG时保存Cls图", false).toBool(); - saveOri = _paramSyncManager->get("通用设置", "存储原图", true).toBool(); - saveRes = _paramSyncManager->get("通用设置", "存储结果图", true).toBool(); - saveSeg = _paramSyncManager->get("通用设置", "存储Seg图", true).toBool(); - saveCls = _paramSyncManager->get("通用设置", "存储Cls图", true).toBool(); - //初始化存图线程 - QMutexLocker locker(&saveThreadMutexVec); - saveThreads.emplace_back(std::make_unique(saveThreads.size())); + + + imgSaveThreadManager(std::string pathIn, paramSyncManager* paramSyncManagerIn, bSignalHub* bridgeIn) + :dbThread(std::make_unique("./sqlDatabase/imgSavePath.db")),//初始化存图DB + saveThread(std::make_unique()),//初始化存图线程 + _ngSummary(new ngSummary(40)), + _paramSyncManager(paramSyncManagerIn), + bridge(bridgeIn), + _ImageSavePathFormatter(std::make_unique()){ + + _ImageSavePathFormatter->rootDir = QString::fromStdString(pathIn);//设置存图根目录 + getParamTimer.start(); + refreshParam();//获取参数设置 } ~imgSaveThreadManager() { + + //防止仍有线程调用saveImage时析构了 等待10S + int sleepCnt = 0; + while (savingImgCount > 0) { + QThread::msleep(100); + sleepCnt++; + if (sleepCnt > 100) + break; + } + // 等待数据库存储线程完成 + if (_ngSummary != nullptr) { + delete _ngSummary; + _ngSummary = nullptr; + } + if (dbThread && dbThread->isRunning()) { qDebug() << "等待数据库存储线程完成..."; dbThread->wait(5000); // 最多等待5秒 @@ -494,144 +685,134 @@ public: dbThread->wait(); } } - - // 等待所有保存线程完成任务 - for (auto& threadPtr : saveThreads) { - if (threadPtr && threadPtr->isRunning()) { - qDebug() << "等待图像保存线程完成..."; - // 等待线程自然结束(队列为空时会自动退出) - threadPtr->wait(3000); // 最多等待3秒 - if (threadPtr->isRunning()) { - qWarning() << "图像保存线程未能在超时时间内完成,强制终止"; - threadPtr->terminate(); - threadPtr->wait(); - } + + // 等待保存线程完成任务 + if (saveThread && saveThread->isRunning()) { + qDebug() << "等待图像保存线程完成..."; + // 等待线程自然结束(队列为空时会自动退出) + saveThread->wait(5000); // 最多等待5秒 + if (saveThread->isRunning()) { + qWarning() << "图像保存线程未能在超时时间内完成,强制终止"; + saveThread->terminate(); + saveThread->wait(); } + } - // 输出未完成任务数量 - if (threadPtr) { - int remainingJobs = threadPtr->getQueueSize(); - if (remainingJobs > 0) { - qWarning() << "析构时仍有" << remainingJobs << "个任务未完成"; - } + // 输出未完成任务数量 + if (saveThread) { + int remainingJobs = saveThread->getQueueSize(); + if (remainingJobs > 0) { + qWarning() << "析构时仍有" << remainingJobs << "个任务未完成"; } } + qDebug() << "imgSaveThreadManager 析构结束"; }; + //供外部调用 void saveImage(std::shared_ptr data) { + savingImgCount++; // 线程安全地获取参数 - std::vector allDirectoryPaths; { QMutexLocker locker(¶mTimerMutex); - if (getParamTimer.elapsed() > 5000) { - saveOri = _paramSyncManager->get("通用设置", "存储原图", true).toBool(); - saveRes = _paramSyncManager->get("通用设置", "存储结果图", true).toBool(); - saveSeg = _paramSyncManager->get("通用设置", "存储Seg图", true).toBool(); - saveCls = _paramSyncManager->get("通用设置", "存储Cls图", true).toBool(); - saveExtraNgImg = _paramSyncManager->get("通用设置", "额外存储NG图片", false).toBool(); - onlySaveNgOri = _paramSyncManager->get("通用设置", "仅在NG时保存原图", false).toBool(); - onlySaveNgSeg = _paramSyncManager->get("通用设置", "仅在NG时保存Seg图", false).toBool(); - onlySaveNgCls = _paramSyncManager->get("通用设置", "仅在NG时保存Cls图", false).toBool(); + //每隔十秒获取一次参数 + if (getParamTimer.elapsed() > 10000) { + refreshParam(); getParamTimer.restart(); } } try { + ngSummary::ngJudgement ngSummaryResult; +#pragma region 获取NG结果判定(开启汇总时会进行等待汇总,否则使用DetData自身的判定结果) + if (enableSummurayNgResult == true) + ngSummaryResult = _ngSummary->waitForResult(QString::fromStdString(data->SNcode), data->aiDetOK, data->halconOK, data->resultSaveText); + else + ngSummaryResult = { data->aiDetOK, data->halconOK, data->resultSaveText }; +#pragma endregion + std::shared_ptr resJob(new saveJob("[RES_JOB]" + data->SNcode)); std::shared_ptr oriJob(new saveJob("[ORI_JOB]" + data->SNcode)); - auto saveThreadState = getSaveThread(); - //生产存储结果图的路径 - QString resSavePath = _ImageSavePathFormatter.genPath(data, "结果图", data->customPath, data->customImageSaveTemplateParams); - //根据预设生成每个文件夹的存储路径并打包 - if (saveRes == true) { - if (createPathCache.contains(resSavePath) == false) {//不存在此文件夹则创建 - createDirectoryIfNotExists(resSavePath); - allDirectoryPaths.push_back(resSavePath);//记录文件夹路径 - createPathCache.put(resSavePath);//加入缓存 - } + //生成存储结果图的路径 + QString resSavePath = _ImageSavePathFormatter->genPath(data->SNcode, data->cameraKeyName, data->saveObjectName, + "结果图", data->customPath, ngSummaryResult, data->customImageSaveTemplateParams); + if (saveRes == true && //保存结果图按钮为启用 + _memUsage.load().usedMemPercent < notSaveAnyImgThreshold) {//内存占用不超过限制 + + createDirIfNotExistsWithCache(resSavePath);//带LRUCache缓存校验的文件夹创建,并将保存路径加入数据库 //结果图保存 if (data->getResultImgSize() != 0) { - std::vectorfileNames = _ImageSavePathFormatter.genFileNames(data, ImageSavePathFormatter::resImg, data->getResultImgVec());//按规则为文件创建命名 + std::vectorfileNames = _ImageSavePathFormatter->genFileNames(data, ImageSavePathFormatter::resImg, data->getResultImgVec());//按规则为文件创建命名 packageData(resJob->imgQueue, resSavePath.toLocal8Bit().toStdString(), fileNames, data->getResultImgVec());//打包数据 } } - //生产存储原图的路径 - QString oriSavePath = _ImageSavePathFormatter.genPath(data, "原图", data->customPath, data->customImageSaveTemplateParams); - //根据预设生成每个文件夹的存储路径并打包 - if (saveOri == true || saveSeg == true || saveCls == true) { + //生成存储原图的路径 + QString oriSavePath = _ImageSavePathFormatter->genPath(data->SNcode, data->cameraKeyName, data->saveObjectName, + "原图", data->customPath, ngSummaryResult ,data->customImageSaveTemplateParams); + if ((saveOri == true || saveSeg == true || saveCls == true) && //保存SEG图/CLS图/原图按钮为启用 + _memUsage.load().usedMemPercent < notSaveOriImgThreshold) {//内存占用不超过限制 + + createDirIfNotExistsWithCache(oriSavePath);//带LRUCache缓存校验的文件夹创建,并将保存路径加入数据库 +#pragma region 原图存储及判断 + if (_memUsage.load().usedMemPercent > notSaveOkOriImgThreshold && //当内存占用超过上限 + ngSummaryResult.aiDetOK == true && ngSummaryResult.halconOK == true) {//且结果为OK时不保存原图 - if (createPathCache.contains(oriSavePath) == false) {//不存在此文件夹则创建 - createDirectoryIfNotExists(oriSavePath); - allDirectoryPaths.push_back(oriSavePath);//记录文件夹路径 - createPathCache.put(oriSavePath);//加入缓存 - } - //当缓存满时[且]无NG时不会存图 - if (saveThreadState.bufferFull && data->aiDetOK == true && data->halconOK == true) { cv::Mat blankImg(300, 300, CV_8UC1, cv::Scalar(0)); cv::putText(blankImg, "buffer full", cv::Point(0, 50), 1, 2, cv::Scalar(255), 1); std::vector blankImgs = { blankImg }; - std::vectorfileNames = _ImageSavePathFormatter.genFileNames(data, ImageSavePathFormatter::oriImg, blankImgs);//按规则为文件创建命名 + std::vectorfileNames = _ImageSavePathFormatter->genFileNames(data, ImageSavePathFormatter::oriImg, blankImgs);//按规则为文件创建命名 packageData(oriJob->imgQueue, oriSavePath.toLocal8Bit().toStdString(), fileNames, blankImgs);//打包数据 } else { - //<只在NG时保存原图>选项开启时任意一个结果为NG时储存[或]<只在NG时保存原图>选项关闭时储存 - if ((onlySaveNgOri && (data->aiDetOK != true || data->halconOK != true)) || onlySaveNgOri == false) { + if ((onlySaveNgOri && (ngSummaryResult.aiDetOK != true || ngSummaryResult.halconOK != true)) ||//<只在NG时保存原图>选项开启且任意一个结果为NG时 + onlySaveNgOri == false) {//<只在NG时保存原图>选项关闭时 //原图 if (data->getOriImgSize() != 0 && saveOri == true) {//原图数量不为0且保存原图选项为TRUE - std::vectorfileNames = _ImageSavePathFormatter.genFileNames(data, ImageSavePathFormatter::oriImg, data->getOriImgVec());//按规则为文件创建命名 + std::vectorfileNames = _ImageSavePathFormatter->genFileNames(data, ImageSavePathFormatter::oriImg, data->getOriImgVec());//按规则为文件创建命名 packageData(oriJob->imgQueue, oriSavePath.toLocal8Bit().toStdString(), fileNames, data->getOriImgVec());//打包数据 } } - if ((onlySaveNgSeg && (data->aiDetOK != true || data->halconOK != true)) || onlySaveNgSeg == false) { - //SEG原图 - if (data->getSegOriginImgSize() != 0 && saveSeg == true) { - std::vectorfileNames = _ImageSavePathFormatter.genFileNames(data, ImageSavePathFormatter::segImg, data->getSegOriginImgVec());//按规则为文件创建命名 - packageData(resJob->imgQueue, oriSavePath.toLocal8Bit().toStdString(), fileNames, data->getSegOriginImgVec());//打包数据 - } + } +#pragma endregion + + if ((onlySaveNgSeg && (ngSummaryResult.aiDetOK != true || ngSummaryResult.halconOK != true)) || + onlySaveNgSeg == false) { + //SEG原图 + if (data->getSegOriginImgSize() != 0 && saveSeg == true) { + std::vectorfileNames = _ImageSavePathFormatter->genFileNames(data, ImageSavePathFormatter::segImg, data->getSegOriginImgVec());//按规则为文件创建命名 + packageData(resJob->imgQueue, oriSavePath.toLocal8Bit().toStdString(), fileNames, data->getSegOriginImgVec());//打包数据 } - if ((onlySaveNgCls && (data->aiDetOK != true || data->halconOK != true)) || onlySaveNgCls == false) { - //CLS原图 - if (data->getClsOriginImgSize() != 0 && saveCls == true) { - std::vectorfileNames = _ImageSavePathFormatter.genClsFileNames(data, data->getClsOriginImgVec());//按规则为文件创建命名 - packageData(resJob->imgQueue, oriSavePath.toLocal8Bit().toStdString(), fileNames, data->getClsOriginImgVec());//打包数据 - } + } + if ((onlySaveNgCls && (ngSummaryResult.aiDetOK != true || ngSummaryResult.halconOK != true)) || + onlySaveNgCls == false) { + //CLS原图 + if (data->getClsOriginImgSize() != 0 && saveCls == true) { + std::vectorfileNames = _ImageSavePathFormatter->genClsFileNames(data, data->getClsOriginImgVec());//按规则为文件创建命名 + packageData(resJob->imgQueue, oriSavePath.toLocal8Bit().toStdString(), fileNames, data->getClsOriginImgVec());//打包数据 } } } //将打包的图片加入存储队列,priority优先存储,多为jpg的结果图,为了尽快释放内存 if (resJob->imgQueue.size() != 0) - saveThreadState.threadPtr->addJob(resJob, imgSaveThread::priority); + saveThread->addJob(resJob, imgSaveThread::priority); if (oriJob->imgQueue.size() != 0) - saveThreadState.threadPtr->addJob(oriJob); + saveThread->addJob(oriJob); //额外单独存储NG图片 - if (saveExtraNgImg&&(data->halconOK == false || data->aiDetOK == false)) { + if (saveExtraNgImg && (ngSummaryResult.halconOK == false || ngSummaryResult.aiDetOK == false)) { std::shared_ptr saveExtraNgJob(new saveJob("[EXTRA_NG_JOB]" + data->SNcode)); - QString extraNgSavePath = _ImageSavePathFormatter.genPath(data, "结果图", _ImageSavePathFormatter.defaultExtraNgSavePath, data->customImageSaveTemplateParams);//获取文件夹名 - if (createPathCache.contains(extraNgSavePath) == false) {//不存在此文件夹则创建 - createDirectoryIfNotExists(extraNgSavePath); - allDirectoryPaths.push_back(extraNgSavePath);//记录文件夹路径 - createPathCache.put(extraNgSavePath);//加入缓存 - } - + QString extraNgSavePath = _ImageSavePathFormatter->genPath(data->SNcode, data->cameraKeyName, data->saveObjectName, + "结果图", _ImageSavePathFormatter->defaultExtraNgSavePath, ngSummaryResult ,data->customImageSaveTemplateParams);//获取文件夹名 + createDirIfNotExistsWithCache(extraNgSavePath);//带LRUCache缓存校验的文件夹创建,并将保存路径加入数据库 //结果图保存 if (data->getResultImgSize() != 0) { - std::vectorfileNames = _ImageSavePathFormatter.genFileNames(data, ImageSavePathFormatter::resImg, data->getResultImgVec());//按规则为文件创建命名 + std::vectorfileNames = _ImageSavePathFormatter->genFileNames(data, ImageSavePathFormatter::resImg, data->getResultImgVec());//按规则为文件创建命名 packageData(saveExtraNgJob->imgQueue, extraNgSavePath.toLocal8Bit().toStdString(), fileNames, data->getResultImgVec());//打包数据 } if (saveExtraNgJob->imgQueue.size() != 0) - saveThreadState.threadPtr->addJob(saveExtraNgJob); + saveThread->addJob(saveExtraNgJob); } -#pragma region 将存储目录写入数据库中 -#if savePathToDataBase - if (!allDirectoryPaths.empty() && dbThread) { - dbThread->addPaths(allDirectoryPaths); - } -#endif -#pragma endregion - } catch (cv::Exception& e) { @@ -641,76 +822,134 @@ public: } catch (...) { qCritical() << "打包存图崩溃 未知异常 SN: " << QString::fromStdString(data->SNcode) << " uuid: " << data->uuid; } + savingImgCount--; } - struct getSaveThreadResult { - imgSaveThread* threadPtr; - bool bufferFull = false; - }; - getSaveThreadResult getSaveThread() { - getSaveThreadResult result; - QMutexLocker locker(&saveThreadMutexVec); - for (auto& threadPtr : saveThreads) { - int threadbufferSize = threadPtr->getThreadBufferSize(); - if (threadbufferSize < imgSaveThreadMaxCacheGigaByte * GigaByte) { - result.threadPtr = threadPtr.get(); - return result; - } - } - // 否则新建线程 - if (saveThreads.size() < maxSaveThreadCount) { - saveThreads.emplace_back(std::make_unique(saveThreads.size())); - qWarning() << "当前存图线程缓存均已超过" << QString::number(imgSaveThreadMaxCacheGigaByte) << "Gb,新建存图线程后当前线程数" << saveThreads.size(); - } - else { - qCritical() << "当前存图线程缓存均已超过" << QString::number(imgSaveThreadMaxCacheGigaByte) << "Gb,且已达最大线程数限制"; - result.bufferFull = true; - } - result.threadPtr = saveThreads.back().get(); - return result; - } private: - LRUCache createPathCache{ 10 };//用于缓存已经创建过的文件夹路径,防止重复创建 + bSignalHub* bridge; + std::atomic savingImgCount = 0; + std::atomic _memUsage;//内存占用 + ngSummary* _ngSummary;//用于汇总NG存图结果 + LRUCache createPathCache{ 100 };//用于缓存已经创建过的文件夹路径,防止重复创建 std::unique_ptr dbThread; // 数据库存储线程 QMutex paramTimerMutex; // 保护参数计时器的互斥锁 - int maxSaveThreadCount = imgSaveTreadMaxCount; - QMutex saveThreadMutexVec; - std::vector> saveThreads;//存图线程池 - bool saveOri = true, saveRes = true, saveSeg = true, saveCls = true, saveExtraNgImg = false - , onlySaveNgOri = false, onlySaveNgSeg = false, onlySaveNgCls = false; + std::unique_ptr saveThread;//存图线程 + bool saveOri = true, saveRes = true, saveSeg = true, saveCls = true, saveExtraNgImg = false, + onlySaveNgOri = false, onlySaveNgSeg = false, onlySaveNgCls = false, + enableSummurayNgResult = false; + double notSaveOkOriImgThreshold = 70.0, notSaveOriImgThreshold = 80.0, notSaveAnyImgThreshold = 90.0; QElapsedTimer getParamTimer;//防止加锁次数过多导致卡顿 - paramSyncManager* _paramSyncManager; - ImageSavePathFormatter _ImageSavePathFormatter; - void packageData(QQueue>& jobQueue, std::string imgSavaPath, const std::vector fileNames, const std::vector& mats) { + paramSyncManager* _paramSyncManager = nullptr; + std::unique_ptr _ImageSavePathFormatter; + void packageData(QQueue>>>& jobQueue, + std::string imgRootSavaPath, + const std::vector fileNames, + const std::vector& mats) { + if (fileNames.size() != mats.size()) qWarning() << "检测到存图文件名数量与图片数量不符"; for (int index = 0; index < fileNames.size() && index < mats.size(); index++) { - if (mats[index].data == nullptr) { - qDebug() << "在存图线程中发现空指针"; - continue; - } + try { + if (mats[index].data == nullptr) { + qDebug() << "在存图线程中发现空指针"; + continue; + } - cv::Mat img; - mats[index].copyTo(img); - jobQueue.enqueue(std::make_pair(imgSavaPath + fileNames[index], img)); + std::vector buffer; + encode(mats[index], buffer, fileNames[index]); + std::shared_ptr> bufferPtr = std::make_shared< std::vector>(); + bufferPtr->swap(buffer); + jobQueue.enqueue(std::make_pair(imgRootSavaPath + fileNames[index], bufferPtr)); + } + catch (...) { + qWarning() << "打包单张图片时失败" << QString::fromStdString(fileNames[index]); + } } } - void packageData(QQueue>& jobQueue, std::string imgSavaPath, const std::vector fileNames, const std::vector>& mats) { + + void packageData(QQueue>>>& jobQueue, + const std::string& imgRootSavaPath, + const std::vector& fileNames, + const std::vector>& mats) { + if (fileNames.size() != mats.size()) qWarning() << "检测到存图文件名数量与图片数量不符"; for (int index = 0; index < fileNames.size() && index < mats.size(); index++) { - if (mats[index].second.data == nullptr) { - qDebug() << "在存图线程中发现空指针"; - continue; + try { + if (mats[index].second.data == nullptr) { + qDebug() << "在存图线程中发现空指针"; + continue; + } + + std::vector buffer; + encode(mats[index].second, buffer, fileNames[index]); + std::shared_ptr> bufferPtr = std::make_shared< std::vector>(); + bufferPtr->swap(buffer); + jobQueue.enqueue(std::make_pair(imgRootSavaPath + fileNames[index], bufferPtr)); + } + catch (...) { + qWarning() << "打包单张图片时失败" << QString::fromStdString(fileNames[index]); } - - cv::Mat img; - mats[index].second.copyTo(img); - jobQueue.enqueue(std::make_pair(imgSavaPath + fileNames[index], img)); } + } + + bool encode(const cv::Mat& img, + std::vector& buffer, + const std::string& path, + const std::vector& params = {}){ + std::string format = path.substr(path.find_last_of(".")); + try { + return cv::imencode(format, img, buffer, params); + } + catch (...) { + return false; + } + } + + //刷新存图参数 + void refreshParam() { + if (_paramSyncManager != nullptr) { + saveExtraNgImg = _paramSyncManager->get("通用设置", "额外存储NG图片", false).toBool(); + onlySaveNgOri = _paramSyncManager->get("通用设置", "仅在NG时保存原图", false).toBool(); + onlySaveNgSeg = _paramSyncManager->get("通用设置", "仅在NG时保存Seg图", false).toBool(); + onlySaveNgCls = _paramSyncManager->get("通用设置", "仅在NG时保存Cls图", false).toBool(); + saveOri = _paramSyncManager->get("通用设置", "存储原图", true).toBool(); + saveRes = _paramSyncManager->get("通用设置", "存储结果图", true).toBool(); + saveSeg = _paramSyncManager->get("通用设置", "存储Seg图", true).toBool(); + saveCls = _paramSyncManager->get("通用设置", "存储Cls图", true).toBool(); + notSaveOkOriImgThreshold = _paramSyncManager->get("通用设置", "仅存储NG图的内存占用上限", 70.0).toDouble(); + notSaveOriImgThreshold = _paramSyncManager->get("通用设置", "仅存储结果图的内存占用上限", 80.0).toDouble(); + notSaveAnyImgThreshold = _paramSyncManager->get("通用设置", "不存储任何图片的内存占用上限", 90.0).toDouble(); + _ngSummary->setWaitTimeSec(_paramSyncManager->get("通用设置", "存图汇总NG结果的等待时间(秒)", 6.0).toDouble()); + enableSummurayNgResult = _paramSyncManager->get("通用设置", "启用等待NG汇总存图", false).toBool(); + _memUsage = getMemUsage(); + auto mem = _memUsage.load(); + if (mem.usedMemPercent > notSaveAnyImgThreshold) { + qDebug() << "内存占用超过" << notSaveAnyImgThreshold << "%,将不储存任何图片"; + } + else if (mem.usedMemPercent > notSaveOriImgThreshold) { + qDebug() << "内存占用超过" << notSaveOriImgThreshold << "%,将不储存原图"; + } + else if (mem.usedMemPercent > notSaveOkOriImgThreshold) { + qDebug() << "内存占用超过" << notSaveOkOriImgThreshold << "%,将不储存OK原图"; + } + } + } + + //带LRUCache缓存校验的文件夹创建,并将保存路径加入数据库 + void createDirIfNotExistsWithCache(QString path) { + if (createPathCache.contains(path) == false) {//不存在此文件夹则创建 + createDirectoryIfNotExists(path);//创建文件夹 + createPathCache.put(path);//将已创建的文件夹路径加入缓存 +#if savePathToDataBase + if (dbThread) { + dbThread->addPath(path); + } +#endif + } } }; diff --git a/src/core/service/missionBase.hpp b/src/core/service/missionBase.hpp index 259b19b..e47f143 100644 --- a/src/core/service/missionBase.hpp +++ b/src/core/service/missionBase.hpp @@ -24,7 +24,6 @@ public: initCustom(); return true; }; - //virtual bool processAlgo(std::shared_ptr data, handleAiModelManager* _handleAiModelManager) { return true; }; virtual QString command(QString& command) { return "NG"; }; void reloadConfig(nlohmann::ordered_json& _missionJson = nlohmann::ordered_json()) { onConfigReloaded(); diff --git a/src/core/service/missionResultHandle.h b/src/core/service/missionResultHandle.h index 28a331c..d88c010 100644 --- a/src/core/service/missionResultHandle.h +++ b/src/core/service/missionResultHandle.h @@ -4,12 +4,13 @@ //为了更集中方便的展示检测结果到主页面 class missionResultHandle { public: - missionResultHandle(bSignalHub* qtBridgeIn, DataRecorder* _dataRecorderIn){ + missionResultHandle(bSignalHub* qtBridgeIn, dataRecorder* _dataRecorderIn){ qtBridge = qtBridgeIn; _dataRecorder = _dataRecorderIn; + timer.start(); } - //defectAndNum大于0则为NG,否则为OK + //defects的数量大于0则为NG,否则为OK void toBarChart(std::string sn,std::vector defects) { QMutexLocker locker(&mutex); if(timer.elapsed() > 3600000) {//每小时清理一次数据,防止内存占用过大 @@ -62,5 +63,5 @@ private: QElapsedTimer timer; QMutex mutex; bSignalHub* qtBridge; - DataRecorder* _dataRecorder; + dataRecorder* _dataRecorder; }; diff --git a/src/core/service/paramSyncManager.h b/src/core/service/paramSyncManager.h index 2943c16..e315b78 100644 --- a/src/core/service/paramSyncManager.h +++ b/src/core/service/paramSyncManager.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include #include diff --git a/src/core/service/serviceManager.h b/src/core/service/serviceManager.h index 7cac850..5cddfc2 100644 --- a/src/core/service/serviceManager.h +++ b/src/core/service/serviceManager.h @@ -5,16 +5,19 @@ #include #include #include +#include +#include class ServiceManager { public: detDataManager* _detDataManager = nullptr;// 检测数据管理器 需要初始化 imgSaveThreadManager* _imgSaveThreadManager = nullptr;// 保存图像线程 需要初始化 triggerManager* _triggerManager = nullptr;// 触发管理器 - cameraManager* _cameraManager = nullptr; - SQLiteDB* dataBase = nullptr; - DataRecorder* _dataRecorder = nullptr; - missionResultHandle* _missionResultHandle = nullptr; - paramSyncManager* _paramSyncManager = nullptr; + cameraManager* _cameraManager = nullptr;//相机管理器 + SQLiteDB* dataBase = nullptr;//主数据库 + dataRecorder* _dataRecorder = nullptr;//按照固定的方式写入数据库 + missionResultHandle* _missionResultHandle = nullptr;//处理算法线程的数据采集 + paramSyncManager* _paramSyncManager = nullptr;//前后端参数实时同步器 + handleAiManager* _handleAiManager = nullptr;;//模型管理器 }; #pragma once \ No newline at end of file diff --git a/src/core/service/summaryData.h b/src/core/service/summaryData.h index 1a32039..854b4b9 100644 --- a/src/core/service/summaryData.h +++ b/src/core/service/summaryData.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include #include @@ -10,7 +10,7 @@ class summaryData { public: summaryData() {} - summaryDataTemplate* get(const std::string& sn) { + summaryDataTemplate* get(const std::string& sn,const summaryDataTemplate&defvalue) { QMutexLocker locker(&_mutex); cleanOldest(100); @@ -22,7 +22,7 @@ public: order.push_back(sn); auto list_it = std::prev(order.end()); - map.emplace(sn, std::make_pair(summaryDataTemplate(), list_it)); + map.emplace(sn, std::make_pair(defvalue, list_it)); return &(map.find(sn)->second.first); } diff --git a/src/core/service/triggerManager.cpp b/src/core/service/triggerManager.cpp index 79710da..e497832 100644 --- a/src/core/service/triggerManager.cpp +++ b/src/core/service/triggerManager.cpp @@ -16,7 +16,7 @@ triggerManager::triggerManager(ServiceManager* _serviceManager, bSignalHub* brid auto splitResult = QString::fromStdString(d).split("_"); _struct.startPos = splitResult[2].split("D")[1].toInt(); _struct.readLength = splitResult[3].toInt(); - readAllCmds.push_back(_struct); + readAllCmds.insert(_struct.readAllCmd,_struct); } triggerFileName = QString::fromStdString(triggerJson["moduleName"]); @@ -232,7 +232,7 @@ void triggerManager::checkUnionCallback(readAllStruct& _struct, QByteArray& byte bool triggerManager::checkTrigger(int dmPos, int value, readAllStruct& _struct, QByteArray& byte, bool lockerDisable) { int triggerPos = (dmPos - _struct.startPos) * 2 + 1; - if (byte.size() < triggerPos) + if (byte.size() <= triggerPos) return false; if (byte.at(triggerPos) == value) { if (tLocker[_struct.readAllCmd].checkLocker(triggerPos) == false) diff --git a/src/core/service/triggerManager.h b/src/core/service/triggerManager.h index 4aac958..1a85c8b 100644 --- a/src/core/service/triggerManager.h +++ b/src/core/service/triggerManager.h @@ -70,12 +70,14 @@ static QByteArray convertStringForFins(const QString& text) { class triggerLockerT { public: // 不存在则返回true - bool checkLocker(int pos) const { + bool checkLocker(int pos) { + QMutexLocker lokcer(&mutex); return lockerVec.find(pos) == lockerVec.end(); } // 如果和加锁的值不同则删除 void reFreshLocker(const QByteArray& input) { + QMutexLocker lokcer(&mutex); for (auto it = lockerVec.begin(); it != lockerVec.end(); ) { if (it->first >= input.size() || input.at(it->first) != it->second) { it = lockerVec.erase(it); // erase 返回下一个迭代器 @@ -88,10 +90,12 @@ public: // 添加锁 void addLocker(int pos, int val) { + QMutexLocker lokcer(&mutex); lockerVec[pos] = val; } private: + QMutex mutex; std::unordered_map lockerVec; }; @@ -104,7 +108,7 @@ public: int startPos; int readLength; }; - std::vector readAllCmds; + QMap readAllCmds;//key:readAllCmd //QByteArray readAllCmd; QString triggerFileName; @@ -235,88 +239,70 @@ public: #pragma endregion #pragma region 接收数据 void receiveCommand(); - QVariant read(int dmPos) { - QByteArray readByte; - mutex.lock(); - if (!sendFinsMsg("read_byte_D" + QString::number(dmPos) + "_2")) - qCritical() << "[TCP] " << " finstcp_ptr reciveData error!"; - if (!reciveFinsMsg(readByte)) - qCritical() << "[TCP] " << " finstcp_ptr readData error!"; - mutex.unlock(); - int val1 = readByte.at(0) * 256; - int val2 = static_cast(readByte.at(1)); - return val1 + val2; - } #pragma endregion //CALL BACK struct triggerCallbackStruct{ - int triggerAddress; - int triggerValue; - triggerCallback func; - std::string funcName; + int triggerAddress;//触发信号 + int triggerValue;//触发的值 + std::string funcName;//函数名 + triggerCallback func;//函数 }; struct unionTriggerCallbackStruct { - std::vector> triggerAddressAndValue; - uniontriggerCallback func; - conditionMode mode; - std::string funcName; + std::vector> triggerAddressAndValue;//联合触发用于判断的地址 + conditionMode mode;//联合判断模式 + std::string funcName;//函数名 + uniontriggerCallback func;//函数 }; - std::string readSnFromQByteArray(QByteArray plcByte, int dmPos, - bool highLowConversion, bool clearAfterRead, int Size) { - int startPos = 0; + //从QBytearray(从plc批量读取的值)中获取sn + std::string readSnFromQByteArray(const QByteArray& plcByte, int snPos, + bool highLowConversion, int minLength, int maxLength = 40) { + // 1. 获取bunchReadStartPos - 即批量读取的起始地址(snPos必须在某个批量读取的范围内,否则无法确定sn在QByteArray中的位置) + int bunchReadStartPos = 0; for (auto& _struct : readAllCmds) { - if (dmPos >= _struct.startPos && dmPos < _struct.startPos + _struct.readLength) - startPos = _struct.startPos; + if (snPos >= _struct.startPos && snPos < _struct.startPos + _struct.readLength) + bunchReadStartPos = _struct.startPos; } - if (startPos == 0) { - qCritical() << dmPos << "读条码越界"; - return "READ_SN_ERROR_OUT_OF_RANGE[" + std::to_string(dmPos) + "]"; + //2.计算snStartPos 为真实SN地址减去读取批量读取起始地址对应在QByteArray中的起始位置 + int snStartPos = (snPos - bunchReadStartPos) * 2; + if (bunchReadStartPos == 0 || snStartPos + 1 >= plcByte.size()) { + qCritical() << snPos << "读条码越界"; + return "READ_SN_ERROR_OUT_OF_RANGE[" + std::to_string(snPos) + "]"; } - int triggerPos = (dmPos - startPos) * 2; - if (triggerPos < 0) { - std::cout << "Read SN ,trigger is DM:" << dmPos << ",but read start at DM:" << startPos << endl; - return "readSnConfigError" + getCurrentDateTime(); + else if (snStartPos < 0) { + std::cout << "Read SN ,trigger is DM:" << snPos << ",but read start at DM:" << bunchReadStartPos << endl; + return "READ_SN_CONFIG_ERROR" + getCurrentDateTime(); } - QString str; - if (plcByte.at(triggerPos + 1) == 0 && plcByte.at(triggerPos) == 0) - return "readSnEmpty" + getCurrentDateTime(); + if (plcByte.at(snStartPos + 1) == 0 && plcByte.at(snStartPos) == 0) + return "READ_SN_EMPTY" + getCurrentDateTime(); + //3.根据高低位转换规则解析SN + QString resultStr; int snCount = 0; - try { - if (highLowConversion == true) { - while (plcByte.at(snCount + triggerPos) != 0 || plcByte.at(snCount + triggerPos + 1) != 0) { + for (int i = 0; i < maxLength && snStartPos + i + 1 < plcByte.size(); ) { - if (plcByte.at(snCount + triggerPos) != 0) - str += QChar(plcByte.at(snCount + triggerPos)); - snCount++; - if (snCount > 40) - break; - } + int pos = snStartPos + i; + char high = plcByte[pos]; + char low = plcByte[pos + 1]; + if (high == 0 && low == 0) + break; + + if (highLowConversion) { + if (high != 0) + resultStr += QChar(high); + i ++; } else { - while (plcByte.at(snCount + triggerPos) != 0 || plcByte.at(snCount + triggerPos + 1) != 0) { - - if (plcByte.at(snCount + triggerPos + 1) != 0) - str += QChar(plcByte.at(snCount + triggerPos + 1)); - if (plcByte.at(snCount + triggerPos) != 0) - str += QChar(plcByte.at(snCount + triggerPos)); - snCount += 2; - if (snCount > 40) - break; - } + if (low != 0) + resultStr += QChar(low); + if (high != 0) + resultStr += QChar(high); + i += 2; } } - catch (...) { - return "snFormatError" + getCurrentDateTime(); - } - if (str.size() < Size && Size != -1) + if (resultStr.size() < minLength && minLength != -1) return "snErrorsnError" + getCurrentDateTime(); - //if (clearAfterRead == true) - // //for(int i=0;i< snCount;i++) - // bridge.back.sendFinsMsg( - //QString::fromStdString("writeALL_DM" + to_string(dmPos) + "_" + to_string(snCount) + "_0")); - return str.toStdString(); + return resultStr.toStdString(); } //单条件的触发器注册 @@ -333,13 +319,14 @@ public: return true; } } - qCritical() << "触发器回调地址超限了" << triggerAddress << triggerValue; + qCritical() << "触发器回调地址超出限制!本次注册无效!" << triggerAddress << triggerValue; + std::cout << "触发器回调地址超出限制!本次注册无效!" << triggerAddress << triggerValue << std::endl; return false; } - std::map> triggerCallbackFuncs; + std::map> triggerCallbackFuncs;//单触发回调函数表 - //联合条件的触发器注册 - bool registerUnionCallback(std::vector> triggerAddressAndValue,conditionMode mode,uniontriggerCallback func, std::string funcName) { + //联合条件的触发器注册 + bool registerUnionCallback(std::vector> triggerAddressAndValue, conditionMode mode, uniontriggerCallback func, std::string funcName) { QMutexLocker locer(&mutex); if (triggerAddressAndValue.size() < 2) { qCritical() << "联合触发器至少需要两个条件"; @@ -370,7 +357,65 @@ public: qInfo() << "注册联合触发器成功:" << ownerStruct->readAllCmd << ", 条件数=" << unionTriggerCallbackFuncs[ownerStruct->readAllCmd].back().triggerAddressAndValue.size(); return true; } - std::map> unionTriggerCallbackFuncs; + std::map> unionTriggerCallbackFuncs;//联合触发回调函数表 + + // 供外部按名称强制触发已注册的回调,不依赖 PLC 当前值。 + bool forceTriggerRegisteredCallback(const std::string& funcName) { + QByteArray byteArray(2000, 0);//保证不会越界 + std::vector normalCallbacks; + std::vector unionCallbacks; + bool triggered = false; + + QMutexLocker locker(&mutex); + for (const auto& callbackGroup : triggerCallbackFuncs) { + for (const auto& callback : callbackGroup.second) { + if (callback.funcName == funcName) { + qDebug() << "force trigger callback:" << QString::fromStdString(callback.funcName) + << callback.triggerAddress << callback.triggerValue; + try { + int triggerPos = (callback.triggerAddress - readAllCmds[callbackGroup.first].startPos) * 2 + 1; + tLocker[callbackGroup.first].addLocker(triggerPos, callback.triggerValue); + callback.func(callback.triggerAddress, callback.triggerValue, byteArray); + triggered = true; + } + catch (const std::exception& e) { + qCritical() << "Error in forced callback " << QString::fromStdString(callback.funcName) << ": " << QString::fromStdString(e.what()); + } + catch (...) { + qCritical() << "Error in forced callback : unexpected" << QString::fromStdString(callback.funcName); + } + } + } + } + for (const auto& callbackGroup : unionTriggerCallbackFuncs) { + for (const auto& callback : callbackGroup.second) { + if (callback.funcName == funcName) { + qDebug() << "force trigger union callback:" << QString::fromStdString(callback.funcName); + try { + for (auto& addresses : callback.triggerAddressAndValue) { + int triggerPos = (addresses.first - readAllCmds[callbackGroup.first].startPos) * 2 + 1; + tLocker[callbackGroup.first].addLocker(triggerPos, addresses.second); + } + callback.func(callback.triggerAddressAndValue, byteArray); + triggered = true; + } + catch (const std::exception& e) { + qCritical() << "Error in forced union callback " << QString::fromStdString(callback.funcName) << ": " << QString::fromStdString(e.what()); + } + catch (...) { + qCritical() << "Error in forced union callback : unexpected" << QString::fromStdString(callback.funcName); + } + } + } + } + + + if (!triggered) { + qWarning() << "未找到可强制触发的回调:" << QString::fromStdString(funcName); + std::cout << "未找到可强制触发的回调: " << funcName << std::endl; + } + return true; + } //监控指定地址的数值 void registerPlcValueViewer(const QString& groupName, const std::vector& viewerDatas) { diff --git a/src/launcher/GUI/mainwindow.cpp b/src/launcher/GUI/mainwindow.cpp index 536e489..c90665d 100644 --- a/src/launcher/GUI/mainwindow.cpp +++ b/src/launcher/GUI/mainwindow.cpp @@ -64,8 +64,6 @@ MainWindow::~MainWindow(){ } - - void MainWindow::initLeftMenuBarComponents() { //主页面 diff --git a/src/launcher/GUI/mainwindow.h b/src/launcher/GUI/mainwindow.h index e11e6c4..306e3f1 100644 --- a/src/launcher/GUI/mainwindow.h +++ b/src/launcher/GUI/mainwindow.h @@ -444,10 +444,27 @@ protected: int ret = dialog.exec(); if (ret == QDialog::Accepted &&(edit.text() == "Y" || edit.text() == "y")){ + QDialog dialogExiting(this); + dialogExiting.setWindowTitle(TR("请稍等,数据保存中")); + dialogExiting.setModal(true); + QVBoxLayout layoutDialogExiting(&dialogExiting); + QLabel labelDialogExiting(QString(TR("程序退出中,正在保存数据,请勿强制结束。"))); + layoutDialogExiting.addWidget(&labelDialogExiting); + dialogExiting.setLayout(&layoutDialogExiting); + dialogExiting.show(); + dialogExiting.raise(); + QApplication::processEvents(); + qDebug() << "手动退出应用程序"; - quitFunction(); - config->saveSettingJson(); + if (quitFunction) { + quitFunction(); + } + if (config) { + config->saveSettingJson(); + } + dialogExiting.close(); event->accept(); + } else { event->ignore(); diff --git a/src/launcher/GUI/pages/displayPageWidget.h b/src/launcher/GUI/pages/displayPageWidget.h index f98d93d..eb24c55 100644 --- a/src/launcher/GUI/pages/displayPageWidget.h +++ b/src/launcher/GUI/pages/displayPageWidget.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -24,6 +24,8 @@ #include #include #include +#include +#include class toolBarWidget : public QWidget { Q_OBJECT public: @@ -188,8 +190,14 @@ public: userSwitch* _userSwitch; softwareDataDisplay* _softwareDataDisplay; runCommandWidget _runCommandWidget; + modelManagerWidget _modelInitWidget; + algoManagerWidget _algoManagerWidget; LogWindow* log = new LogWindow(); - displayWidget(appConfig* config) :m_config(config), _runCommandWidget(this){ + displayWidget(appConfig* config) :m_config(config), + _runCommandWidget(this), + _modelInitWidget(this), + _algoManagerWidget(this){ + this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); this->setMaximumSize(9999, 9999); @@ -336,6 +344,28 @@ public: BindTRLambda([btnPtr]() {btnPtr->setText(TR("故障排查")); }); } #pragma endregion +#pragma region 模型管理 + { + auto btnPtr = _toolBarWidget->createToolButton(QIcon(":/icon/icon/aiModelManage_w.png"), + TR("模型管理"), + [=]() { + _modelInitWidget.show(); + _modelInitWidget.raise(); + }, Auth::UserLevel::ROOT); + BindTRLambda([btnPtr]() {btnPtr->setText(TR("模型管理")); }); + } +#pragma endregion +#pragma region 算法管理 + { + auto btnPtr = _toolBarWidget->createToolButton(QIcon(":/icon/icon/algoManage_w.png"), + TR("算法管理"), + [=]() { + _algoManagerWidget.show(); + _algoManagerWidget.raise(); + }, Auth::UserLevel::ROOT); + BindTRLambda([btnPtr]() {btnPtr->setText(TR("算法管理")); }); + } +#pragma endregion #pragma region 读取bin目录下的tools文件夹并展示 { std::vector files; diff --git a/src/launcher/GUI/pages/settingWidget.h b/src/launcher/GUI/pages/settingWidget.h index 3a22b23..01189c9 100644 --- a/src/launcher/GUI/pages/settingWidget.h +++ b/src/launcher/GUI/pages/settingWidget.h @@ -385,6 +385,22 @@ public: }); consoleMainLayout->addWidget(btn); } + { + fancyButtonText* btn = new fancyButtonText(TR("一键替换当前所有相机为虚拟相机(屏蔽物理相机)")); + consoleMainLayout->addWidget(btn); + connect(btn, &QPushButton::clicked, [=]() { + const auto reply = QMessageBox::question( + this, + TR("确认"), + TR("确定要替换吗,在调试结束后哦请重启软件"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (reply == QMessageBox::Yes) { + emit reloadAllCameraAsVirtual(); + } + }); + consoleMainLayout->addWidget(btn); + } m_layout->addWidget(GroupBox); } // 添加底部伸缩空间 @@ -397,7 +413,8 @@ public: Title->setText(TR("设置")); }); } -protected: +signals: + void reloadAllCameraAsVirtual(); private: appConfig* _config; diff --git a/src/launcher/GUI/pages/sqlControlWidget.h b/src/launcher/GUI/pages/sqlControlWidget.h index fa5dcb3..4378205 100644 --- a/src/launcher/GUI/pages/sqlControlWidget.h +++ b/src/launcher/GUI/pages/sqlControlWidget.h @@ -14,7 +14,7 @@ class sqliteReader : public QWidget{ Q_OBJECT public: - sqliteReader(SQLiteDB* dbIn):db(dbIn) { + sqliteReader(SQLiteDB* dbIn):db(dbIn) { QFont font("Microsoft YaHei", 12, QFont::Bold); QPalette palette; palette.setColor(QPalette::WindowText, Qt::white); @@ -65,7 +65,7 @@ public: tableSelectComboBox->clear(); auto tables = db->getTables(); for (int i = tables.size() - 1; i > 0 ; i--){ - if (!tables[i].contains("set")) + if (!tables[i].contains("set") && !tables[i].contains("hide")) tableSelectComboBox->addItem(tables[i]); } } diff --git a/src/launcher/GUI/uiModule/components/ScrollingLabel.h b/src/launcher/GUI/uiModule/components/ScrollingLabel.h new file mode 100644 index 0000000..f52e375 --- /dev/null +++ b/src/launcher/GUI/uiModule/components/ScrollingLabel.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include + +class ScrollingLabel : public QLabel { +public: + explicit ScrollingLabel(QWidget* parent = nullptr) : QLabel(parent) { + setAlignment(Qt::AlignVCenter | Qt::AlignLeft); + scrollTimer.setInterval(35); + connect(&scrollTimer, &QTimer::timeout, this, [this]() { + if (!scrollEnabled || fullText.isEmpty()) { + return; + } + scrollOffset += 1; + const int loopWidth = textPixelWidth + gap; + if (scrollOffset >= loopWidth) { + scrollOffset = 0; + } + update(); + }); + } + + void setScrollText(const QString& text) { + fullText = text; + updateScrollState(); + } + +protected: + void resizeEvent(QResizeEvent* event) override { + QLabel::resizeEvent(event); + updateScrollState(); + } + + void paintEvent(QPaintEvent* event) override { + if (!scrollEnabled) { + QLabel::paintEvent(event); + return; + } + + Q_UNUSED(event); + QPainter painter(this); + painter.setRenderHint(QPainter::TextAntialiasing, true); + painter.setPen(palette().color(QPalette::WindowText)); + painter.setFont(font()); + + const QFontMetrics fm(font()); + const int baseline = (height() + fm.ascent() - fm.descent()) / 2; + const int x = -scrollOffset; + + painter.drawText(x, baseline, fullText); + painter.drawText(x + textPixelWidth + gap, baseline, fullText); + } + +private: + void updateScrollState() { + textPixelWidth = fontMetrics().horizontalAdvance(fullText); + const int contentWidth = contentsRect().width(); + scrollEnabled = !fullText.isEmpty() && contentWidth > 0 && textPixelWidth > contentWidth; + + if (scrollEnabled) { + if (!scrollTimer.isActive()) { + scrollTimer.start(); + } + update(); + } + else { + if (scrollTimer.isActive()) { + scrollTimer.stop(); + } + scrollOffset = 0; + QLabel::setText(fullText); + } + } + + QTimer scrollTimer; + QString fullText; + int scrollOffset = 0; + int textPixelWidth = 0; + int gap = 36; + bool scrollEnabled = false; +}; \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/components/dayShiftTimer.h b/src/launcher/GUI/uiModule/components/dayShiftTimer.h new file mode 100644 index 0000000..2cd5161 --- /dev/null +++ b/src/launcher/GUI/uiModule/components/dayShiftTimer.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class dayShiftTimer { +public: + struct isDayShiftResult { + bool isDay; // 当前是否白班 + bool isShiftChanged; // 是否发生切换 + QString shiftDate; // 班次日期 + }; + + dayShiftTimer() { + cachedDateTime = QDateTime::currentDateTime(); + timeCacheTimer.start(); + const QTime now = cachedDateTime.time(); + const int minutes = now.hour() * 60 + now.minute(); + bool isDay = (minutes >= (8 * 60 + 30) && minutes < (20 * 60 + 30)); + QString shiftDate = calcShiftDate(cachedDateTime, isDay); + lastIsDayState = isDay; + lastDayShiftDate = shiftDate; + } + + // 主调用接口 + isDayShiftResult update() { + QMutexLocker locker(&mutex); + + // 降频获取时间 + if (!timeCacheTimer.isValid() || timeCacheTimer.elapsed() > intervalMs) { + cachedDateTime = QDateTime::currentDateTime(); + timeCacheTimer.restart(); + } + + const QTime now = cachedDateTime.time(); + const int minutes = now.hour() * 60 + now.minute(); + + bool isDay = (minutes >= (8 * 60 + 30) && minutes < (20 * 60 + 30)); + + QString shiftDate = calcShiftDate(cachedDateTime, isDay); + + bool changed = false; + + // 判断是否切换 + if (isDay != lastIsDayState || shiftDate != lastDayShiftDate) { + changed = true; + lastIsDayState = isDay; + lastDayShiftDate = shiftDate; + + } + + return { isDay, changed, shiftDate }; + } + +private: + + // 计算日期 + QString calcShiftDate(const QDateTime& dt, bool isDay) { + QDate date = dt.date(); + + //夜班0.00~8.30为前一天的夜班 + if (!isDay) { + if (dt.time().hour() < 8 || + (dt.time().hour() == 8 && dt.time().minute() < 30)) { + date = date.addDays(-1); + } + } + + return date.toString("yyyy-MM-dd"); + } + +private: + QMutex mutex; + + bool lastIsDayState = false; + QString lastDayShiftDate; + + QDateTime cachedDateTime; + int intervalMs = 10000; // 时间获取频率(毫秒) + QElapsedTimer timeCacheTimer; // 控制时间获取频率 +}; \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/components/dragPathLineEdit.h b/src/launcher/GUI/uiModule/components/dragPathLineEdit.h new file mode 100644 index 0000000..1d22b69 --- /dev/null +++ b/src/launcher/GUI/uiModule/components/dragPathLineEdit.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +// Read-only path input that accepts a dragged local file or folder. +class dragPathLineEdit : public QLineEdit { +public: + explicit dragPathLineEdit(QWidget* parent = nullptr) : QLineEdit(parent) { + setAcceptDrops(true); + setReadOnly(true); + setPlaceholderText(TR("将文件或文件夹拖入此处,自动获取路径")); + setStyleSheet( + "QLineEdit {" + " background-color: #0F172A;" + " color: #E5E7EB;" + " border: 1px solid #334155;" + " border-radius: 8px;" + " padding: 8px 10px;" + "}" + "QLineEdit:focus {" + " border: 1px solid #38BDF8;" + "}" + ); + } + +protected: + void dragEnterEvent(QDragEnterEvent* event) override { + if (event->mimeData() && event->mimeData()->hasUrls()) { + event->acceptProposedAction(); + return; + } + QLineEdit::dragEnterEvent(event); + } + + void dropEvent(QDropEvent* event) override { + if (!event->mimeData() || !event->mimeData()->hasUrls() || event->mimeData()->urls().isEmpty()) { + QLineEdit::dropEvent(event); + return; + } + + // Only consume the first dropped local path to keep the field deterministic. + const QUrl firstUrl = event->mimeData()->urls().first(); + const QString localPath = firstUrl.toLocalFile(); + if (localPath.isEmpty()) { + QLineEdit::dropEvent(event); + return; + } + + setText(QDir::toNativeSeparators(localPath)); + event->acceptProposedAction(); + } +}; \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/components/readme.txt b/src/launcher/GUI/uiModule/components/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/launcher/GUI/uiModule/components/valueViewer.h b/src/launcher/GUI/uiModule/components/valueViewer.h new file mode 100644 index 0000000..7263ee7 --- /dev/null +++ b/src/launcher/GUI/uiModule/components/valueViewer.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Small widget bundle used by the PLC viewer list to show one titled group of values. +class valueViewer : public QWidget { +public: + QFont font1 = QFont("微软雅黑", 12, QFont::Bold); + QFont font2 = QFont("微软雅黑", 10, QFont::Bold); + QFont font3 = QFont("微软雅黑", 6, QFont::Bold); + + valueViewer(const QString& groupNameIN, + const std::vector& viewerNames, + QMap>& plcViwerLineedits) + : groupName(groupNameIN) { + + QLabel* groupNameLabel = new QLabel(groupNameIN); + groupNameLabel->setFont(font1); + groupNameLabel->setStyleSheet("color: white;"); + + layout->addWidget(groupNameLabel); + for (auto& viewerName : viewerNames) { + plcViwerLineedits[viewerName].push_back(addViewer(viewerName)); + } + widget->setMaximumHeight(900); + } + valueViewer(const QString& groupNameIN, + const std::vector>& viewerNamesAndAddress, + QMap>& plcViwerLineedits) + : groupName(groupNameIN) { + + QLabel* groupNameLabel = new QLabel(groupNameIN); + groupNameLabel->setFont(font1); + groupNameLabel->setStyleSheet("color: white;"); + + layout->addWidget(groupNameLabel); + for (auto& viewerName : viewerNamesAndAddress) { + plcViwerLineedits[viewerName.second].push_back(addViewer(viewerName.first)); + } + widget->setMaximumHeight(900); + } + QLabel* addViewer(const QString& viewerName) { + map.insert(groupName + viewerName, new QLabel("*")); + auto label = map[groupName + viewerName]; + label->setFont(font2); + label->setStyleSheet("color: white;"); + + auto textLabel = new QLabel(viewerName); + textLabel->setFont(font3); + textLabel->setStyleSheet("color: white;"); + + QVBoxLayout* viewerLayout = new QVBoxLayout(); + viewerLayout->addWidget(label, Qt::AlignCenter); + viewerLayout->addWidget(textLabel, Qt::AlignCenter); + + layout->addLayout(viewerLayout); + return label; + } + + QString groupName = ""; + // Exposed because the list view embeds this inner widget directly via setIndexWidget. + QWidget* widget = new QWidget(); + QHBoxLayout* layout = new QHBoxLayout(widget); + QMap map; +}; diff --git a/src/launcher/GUI/uiModule/uiModule_histogramWidget.h b/src/launcher/GUI/uiModule/uiChart_histogramWidget.h similarity index 83% rename from src/launcher/GUI/uiModule/uiModule_histogramWidget.h rename to src/launcher/GUI/uiModule/uiChart_histogramWidget.h index a3857ff..d89650a 100644 --- a/src/launcher/GUI/uiModule/uiModule_histogramWidget.h +++ b/src/launcher/GUI/uiModule/uiChart_histogramWidget.h @@ -14,16 +14,19 @@ #include #include #include - +#include +#include +#include QT_CHARTS_USE_NAMESPACE class histogramWidget : public QWidget { Q_OBJECT public: explicit histogramWidget(const QString& title, QWidget* parent = nullptr) - : QWidget(parent), enableAnimation(false) + : QWidget(parent), enableAnimation(false), currentShift(title) { setStyleSheet("background-color: rgb(50, 55, 60); color: white;"); + initUI(title); initChart(); @@ -31,24 +34,33 @@ public: setStats(okCnt + ngCnt, ngCnt, okCnt); clearButton->setText(TR("清零")); }); + + loadCacheData(); } void addDefect(int modifyOkData, int modiftNgData, std::vectordefects) { okCnt += modifyOkData; ngCnt += modiftNgData; setStats(okCnt + ngCnt, ngCnt, okCnt); - if (defects.empty()) - return; - QMutexLocker locker(&defectMapMutex); - for (auto& defect : defects) { - defectMap[defect]++; - if (defectMap[defect] > 1) - updateSingleBar(QString::fromStdString(defect), defectMap[defect]); - else - addBarCategory(QString::fromStdString(defect), defectMap[defect]); + if (!defects.empty()) { + QMutexLocker locker(&defectMapMutex); + for (auto& defect : defects) { + defectMap[defect]++; + if (defectMap[defect] > 1) + updateSingleBar(QString::fromStdString(defect), defectMap[defect]); + else + addBarCategory(QString::fromStdString(defect), defectMap[defect]); + } } - + appConfig::getInstance().cacheStore->set( + runtimeDataCacheStore::Table::RuntimeCache, + currentShift, + okCnt, + ngCnt, + defectMap + ); } void setTitle(const QString& text) { + currentShift = text; titleLabel->setText(text); } @@ -105,6 +117,7 @@ public: } private: + QString currentShift; QMutex mutex; QLabel* titleLabel; QPushButton* clearButton; @@ -238,6 +251,7 @@ private: } } + //更新左侧最大最小轴 void updateAxisYRange() { if (barSet->count() == 0) { axisY->setRange(0, 10); @@ -248,7 +262,7 @@ private: if (barSet->at(i) > maxVal) maxVal = barSet->at(i); } - axisY->setRange(0, std::ceil(maxVal * 1.1)); // 多留10%空间 + axisY->setRange(0, std::max(4.0, std::ceil(maxVal * 1.1))); // 多留10%空间 } void setStats(int total, int bad, int good) { @@ -256,7 +270,24 @@ private: labelLeft->setText(QString(TR("检测总数") + ": %1\n" + TR("不良数") + ": %2").arg(total).arg(bad)); labelRight->setText(QString(TR("良品数") + ": %1\n" + TR("良品率") + ": %2%").arg(good).arg(goodRate, 0, 'f', 2)); } - + // 从缓存加载数据 + void loadCacheData() { + int tempOkCnt = 0, tempNgCnt = 0; + std::map tmpDefectMap; + appConfig::getInstance().cacheStore->get( + runtimeDataCacheStore::Table::RuntimeCache, + currentShift, + tempOkCnt, + tempNgCnt, + tmpDefectMap + ); + std::vectordefects; + for (const auto& [defect, count] : tmpDefectMap) { + for (int i = 0; i < count; i++) + defects.push_back(defect); + } + addDefect(tempOkCnt, tempNgCnt, defects); + } QMutex buttomBarSetTipLabelMutex;//操作buttomBarSetTipLabel用的锁 QLabel buttomBarSetTipLabel;//窗口底部用于展示柱状图名称以及数量的label QMutex defectMapMutex; @@ -271,6 +302,10 @@ protected: painter.setPen(Qt::NoPen); painter.drawRoundedRect(rect(), 10, 10); } + void resizeEvent(QResizeEvent* event) { + QWidget::resizeEvent(event); + QTimer::singleShot(100, this, [=] { updateValueLabels(); }); + } }; class dayNightShiftHistogramWidget : public QWidget { @@ -293,28 +328,23 @@ public: nightHistogramWidget->setTitle(ymd + TR("夜班") + "(20:30-08:30)"); }); } - //defects为空 输入为修改量 void addDefect(int modifyOkData,int modiftNgData ,std::vectordefects) { QMutexLocker locker(&mutex); - bool isDay = isDayShiftNow(); - QString ymd = QDate::currentDate().toString("yyyy-MM-dd"); + auto dayShiftResult = _dayShiftTimer.update(); //判断是否切换班次,如切换则清空当前班次的数据 - if (lastIsDayState != isDay || lastDayShiftDate != ymd) { - if (isDay == true) { + if (dayShiftResult.isShiftChanged) { + if (dayShiftResult.isDay == true) { dayHistogramWidget->clearAllData(); - dayHistogramWidget->setTitle(ymd + TR("白班") + "(08:30-20:30)"); - lastDayShiftDate = ymd; + dayHistogramWidget->setTitle(dayShiftResult.shiftDate + TR("白班") + "(08:30-20:30)"); } else { nightHistogramWidget->clearAllData(); - nightHistogramWidget->setTitle(ymd + TR("夜班") + "(20:30-08:30)"); - lastDayShiftDate = ymd; + nightHistogramWidget->setTitle(dayShiftResult.shiftDate + TR("夜班") + "(20:30-08:30)"); } } - lastIsDayState = isDay; - if (isDay == true) { + if (dayShiftResult.isDay == true) { dayHistogramWidget->addDefect(modifyOkData, modiftNgData, defects); } else { @@ -323,19 +353,6 @@ public: } private: QMutex mutex; - bool lastIsDayState; - QString lastDayShiftDate; - // 传入任意时间判断 - inline bool isDayShift(const QTime& t) noexcept { - const int m = t.hour() * 60 + t.minute(); - return m >= (8 * 60 + 30) && m < (20 * 60 + 30); - } - - // 判断此刻是否白班 - inline bool isDayShiftNow() noexcept { - const QTime now = QTime::currentTime(); // 本地时间 - const int m = now.hour() * 60 + now.minute(); - return m >= (8 * 60 + 30) && m < (20 * 60 + 30); - } + dayShiftTimer _dayShiftTimer; }; #pragma once diff --git a/src/launcher/GUI/uiModule/uiModule_algoManager.h b/src/launcher/GUI/uiModule/uiModule_algoManager.h new file mode 100644 index 0000000..21ed407 --- /dev/null +++ b/src/launcher/GUI/uiModule/uiModule_algoManager.h @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class algoInitConfirmDialog : public QDialog { +public: + explicit algoInitConfirmDialog(const QString& tip, QWidget* parent = nullptr) : QDialog(parent) { + setWindowTitle(TR("提示:算法文件")); + setModal(true); + resize(680, 420); + setObjectName("modelInitConfirmDialog"); + setStyleSheet(QString( + "QDialog#modelInitConfirmDialog {" + " background-color: rgb(%1);" + " border: 1px solid #1F2937;" + " border-radius: 12px;" + "}" + "QLabel {" + " color: #E5E7EB;" + " font-size: 13px;" + "}" + "QTextEdit {" + " background-color: #111827;" + " color: #E5E7EB;" + " border: 1px solid #374151;" + " border-radius: 10px;" + " padding: 8px;" + "}" + "QPushButton {" + " min-width: 92px;" + " min-height: 32px;" + " border: none;" + " border-radius: 8px;" + " font-weight: 600;" + "}" + "QPushButton:enabled {" + " background-color: #0EA5E9;" + " color: #FFFFFF;" + "}" + "QPushButton:enabled:hover {" + " background-color: #0284C7;" + "}" + "QPushButton:disabled {" + " background-color: #6B7280;" + " color: #D1D5DB;" + "}").arg(sColor_Blue) + ); + + QVBoxLayout* layout = new QVBoxLayout(this); + + QTextEdit* contentText = new QTextEdit(this); + contentText->setReadOnly(true); + contentText->setPlainText(TR("方法:\n" + "将.HDPL文件拖入框内点击确认后自动进行备份及更新\n" + "备份目录为./halconAlgo/backup\n" + "注意:\n" + "在替换结束后需要观察至少20个料的检测情况!\n" + "当前模型详情:\n") + tip); + layout->addWidget(contentText); + + QLabel* dragHintLabel = new QLabel(TR("拖入文件/文件夹以获取目录:"), this); + layout->addWidget(dragHintLabel); + + pathEdit = new dragPathLineEdit(this); + layout->addWidget(pathEdit); + + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, this); + QPushButton* yesBtn = buttonBox->button(QDialogButtonBox::Yes); + QPushButton* noBtn = buttonBox->button(QDialogButtonBox::No); + yesBtn->setText(TR("确认")); + noBtn->setText(TR("取消")); + yesBtn->setEnabled(false); + + connect(pathEdit, &QLineEdit::textChanged, this, [yesBtn](const QString& text) { + yesBtn->setEnabled(!text.trimmed().isEmpty()); + }); + + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + layout->addWidget(buttonBox); + } + + QString selectedDir() const { + return pathEdit ? pathEdit->text().trimmed() : QString(); + } + +private: + dragPathLineEdit* pathEdit = nullptr; +}; + +class algoManagerWidget : public QDialog { + Q_OBJECT +public: + QFont font1 = QFont("微软雅黑", 12, QFont::Bold); + explicit algoManagerWidget(QWidget* parent = nullptr) + : QDialog(parent) { + + this->setWindowTitle(TR("执行命令")); + this->setObjectName("modelManagerWidget"); + this->setStyleSheet(QString("QDialog#modelManagerWidget {background-color: rgb(%1);border: 2px solid #000000;border-radius: 6px;}").arg(sColor_Blue)); + this->setAttribute(Qt::WA_QuitOnClose, false); + + //左侧按钮列表 + btnWidgets = new QListWidget(this); + btnWidgets->setStyleSheet("background: transparent;"); + + //右侧耗时数值监控列表 + timeCostViewerList = new QListView(this); + timeCostViewerList->setStyleSheet("background: transparent;"); + timeCostViewerList->setEditTriggers(QAbstractItemView::NoEditTriggers); + timeCostViewerList->setSelectionMode(QAbstractItemView::NoSelection); + timeCostViewerList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + timeCostViewerList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + timeCostViewerList->setResizeMode(QListView::Adjust); + + timeCostViewerModel = new QStandardItemModel(timeCostViewerList); + timeCostViewerList->setModel(timeCostViewerModel); + + QGridLayout* layout = new QGridLayout(this); + layout->addWidget(btnWidgets, 0, 0); + layout->addWidget(timeCostViewerList, 0, 1); + this->setFixedSize(sizeX, sizeY); + + QListWidgetItem* item = new QListWidgetItem(btnWidgets); + btnWidgets->addItem(item); + fancyButtonText* funcBtn = new fancyButtonText("备份并替换算法文件", 0, 0, "备份并替换算法文件"); + btnWidgets->setItemWidget(item, funcBtn); + + connect(funcBtn, &fancyButtonText::clicked, this, [this]() { + algoInitConfirmDialog dialog("更新算法文件", this); + const int ret = dialog.exec(); + if (ret == QDialog::Accepted) { + const QString selectedDir = dialog.selectedDir(); + if (selectedDir.isEmpty()) { + QMessageBox::warning(this, TR("提示"), TR("请先拖入文件或文件夹,获取目录后再确认。")); + return; + } + QMessageBox::StandardButton reply; + reply = QMessageBox::question( + this, // parent + TR("提示"), // title + TR("确定要执行此操作吗\n路径:"+ selectedDir.toStdString()), // content + QMessageBox::Yes | QMessageBox::No // buttons + ); + if (reply == QMessageBox::Yes) { + emit backupAndReplaceAlgoFile(selectedDir.toStdString()); + } + } + }); + } +signals: + void backupAndReplaceAlgoFile(const std::string& path); +public slots: + void addTimeCostViewer(const QString& groupName, const std::vector& viewerNames) { + if (!timeCostViwerLineEdits.contains(groupName)) { + auto viewer = new valueViewer(groupName, viewerNames, timeCostViwerLineEdits[groupName]); + // 添加到 QListView 中 + QStandardItem* item = new QStandardItem(); + // 根据内部 widget 的 sizeHint 设置行高 + item->setSizeHint(viewer->widget->sizeHint()); + timeCostViewerModel->appendRow(item); + QModelIndex index = timeCostViewerModel->index(timeCostViewerModel->rowCount() - 1, 0); + timeCostViewerList->setIndexWidget(index, viewer->widget); + xExtraSize = std::max(groupName.size() * 20, xExtraSize); + this->setFixedSize(sizeX + xExtraSize, sizeY); + } + } + void refreshTimeCostViewer(const QString& groupName, const QString& avgTime, const QString& maxTime, const QString& minTime) { + if (timeCostViwerLineEdits[groupName]["平均耗时"].size() > 0) + timeCostViwerLineEdits[groupName]["平均耗时"][0]->setText(avgTime); + if (timeCostViwerLineEdits[groupName]["最大耗时"].size() > 0) + timeCostViwerLineEdits[groupName]["最大耗时"][0]->setText(maxTime); + if (timeCostViwerLineEdits[groupName]["最小耗时"].size() > 0) + timeCostViwerLineEdits[groupName]["最小耗时"][0]->setText(minTime); + + } +protected: + //重写showEvent,使窗口显示在屏幕中心 + void showEvent(QShowEvent* event) override { + QWidget::showEvent(event); + //获取屏幕的几何信息 + QScreen* screen = QGuiApplication::primaryScreen(); + QRect screenGeometry = screen->geometry(); + + int x = (screenGeometry.width() - this->width()) / 2; + int y = (screenGeometry.height() - this->height()) / 2; + this->move(x, y); + } + +private: + int sizeX = 1100, sizeY = 720, xExtraSize = 0; + QListWidget* btnWidgets; + QListView* timeCostViewerList; + QStandardItemModel* timeCostViewerModel; + QMap< QString, QMap>>timeCostViwerLineEdits; +}; +#pragma once \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/uiModule_dsStatsWidget.h b/src/launcher/GUI/uiModule/uiModule_dsStatsWidget.h index 4cc59be..4e2ab32 100644 --- a/src/launcher/GUI/uiModule/uiModule_dsStatsWidget.h +++ b/src/launcher/GUI/uiModule/uiModule_dsStatsWidget.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include class dsStatsWidget : public QWidget { Q_OBJECT public: @@ -70,22 +72,39 @@ public: "QLabel { color: white; } " ); BindTR(dsStatsWidget::onLanguageChanged);//当语言更改时触发槽函数 + auto dayShiftResult = _dayShiftTimer.update(); + currentShift = dayShiftResult.shiftDate; + loadCacheData(); } void modifyData(int modifyOkData,int modiftNgData) { QMutexLocker locker(&mutex); + auto dayShiftResult = _dayShiftTimer.update(); + //如果发生了班次切换,并且当前是白班,才进行数据切换 + if (dayShiftResult.isShiftChanged && dayShiftResult.isDay == true) { + appConfig::getInstance().cacheStore->set( + runtimeDataCacheStore::Table::Statistics, + currentShift, + okCnt, + ngCnt, + QMap() + ); + okCnt = 0; + ngCnt = 0; + currentShift = dayShiftResult.shiftDate; + } okCnt += modifyOkData; ngCnt += modiftNgData; setStats(okCnt + ngCnt, okCnt, ngCnt); + appConfig::getInstance().cacheStore->set( + runtimeDataCacheStore::Table::Statistics, + currentShift, + okCnt, + ngCnt, + QMap() + ); } - void setStats(int total, int good, int ng) { - totalEdit->setText(QString::number(total)); - okEdit->setText(QString::number(good)); - ngEdit->setText(QString::number(ng)); - double rate = total > 0 ? (double)good / total : 0.0; - rateEdit->setText(QString::number(rate * 100, 'f', 1) + "%"); - pie->setGoodRate(rate, total > 0); - } + protected: void paintEvent(QPaintEvent* event) override { QPainter painter(this); @@ -103,13 +122,34 @@ protected: } private: + void setStats(int total, int good, int ng) { + totalEdit->setText(QString::number(total)); + okEdit->setText(QString::number(good)); + ngEdit->setText(QString::number(ng)); + double rate = total > 0 ? (double)good / total : 0.0; + rateEdit->setText(QString::number(rate * 100, 'f', 1) + "%"); + pie->setGoodRate(rate, total > 0); + } void clearAllData() { QMutexLocker locker(&mutex); okCnt = 0; ngCnt = 0; setStats(0, 0, 0); } - + void loadCacheData() { + int tempOkCnt = 0, tempNgCnt = 0; + std::map tmpDefectMap; + appConfig::getInstance().cacheStore->get( + runtimeDataCacheStore::Table::Statistics, + currentShift, + tempOkCnt, + tempNgCnt, + tmpDefectMap + ); + modifyData(tempOkCnt, tempNgCnt); + } + QString currentShift; + dayShiftTimer _dayShiftTimer; QLineEdit* createEdit() { QLineEdit* edit = new QLineEdit; edit->setReadOnly(true); @@ -138,11 +178,78 @@ public slots: } }; +class memUsageLabel :public QLabel { +public: + memUsageLabel() { + timer = new QTimer(this); + this->setText("Mem 0.0%"); + connect(timer, &QTimer::timeout, this, [this]() mutable { + setUseage(getMemUsage()); + }); + timer->setInterval(3000); + timer->start(); + } + ~memUsageLabel() { + delete timer; + } + void setUseage(double useage) { + this->setText("Mem " + QString::number(useage, 'f', 1) + "%"); + if (useage > 85) { + this->setStyleSheet("QLabel{" + "background-color: transparent;" + "font-family: 'Microsoft YaHei';" + "font-weight: bold;" + "color: rgb(232, 17, 35);" + "border: none;" + "padding: 4px;" + "}"); + } + else if (useage > 60) { + this->setStyleSheet("QLabel{" + "background-color: transparent;" + "font-family: 'Microsoft YaHei';" + "font-weight: bold;" + "color: rgb(223, 185, 146);" + "border: none;" + "padding: 4px;" + "}"); + } + else if (useage > 45) { + this->setStyleSheet("QLabel{" + "background-color: transparent;" + "font-family: 'Microsoft YaHei';" + "font-weight: bold;" + "color: #ffffff;" + "border: none;" + "padding: 4px;" + "}"); + } + else { + this->setStyleSheet("QLabel{" + "background-color: transparent;" + "font-family: 'Microsoft YaHei';" + "font-weight: bold;" + "color: rgb(17, 232, 35);" + "border: none;" + "padding: 4px;" + "}"); + } + } +private: + static double getMemUsage() { + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(memInfo); + + if (GlobalMemoryStatusEx(&memInfo)) + { + return ((double)(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile) / (double)memInfo.ullTotalPageFile) * 100; + } + return -1.0; + } + QTimer* timer = nullptr; +}; class stateLabel :public QLabel { public: - QPixmap okImg, errorImg; - QTimer* timer = nullptr; - QPixmap transparent; bool blink = false; stateLabel(QString okImgPath,QString errorImgPath, QWidget* parent = nullptr) { okImg = QPixmap(okImgPath).scaled(25, 25, Qt::KeepAspectRatio, Qt::SmoothTransformation); @@ -177,17 +284,20 @@ public: if (!timer->isActive()) timer->start(600); } +private: + QPixmap okImg, errorImg; + QTimer* timer = nullptr; + QPixmap transparent; }; class ConnectionWidget : public QWidget { public: + memUsageLabel* _memUsageLabel = new memUsageLabel(); QHBoxLayout* layout = new QHBoxLayout(this); stateLabel* lanConnectonLabel = new stateLabel(":/icon/icon/plc_connection_ok.png", ":/icon/icon/plc_connection_error.png",this); stateLabel* camConnectonLabel = new stateLabel(":/icon/icon/cam_connection_ok.png", ":/icon/icon/cam_connection_error.png", this); - QPixmap lanConnectionOk, lanConnectionError, camConnectionOK, camConnectionError; - QTimer* timer1 = nullptr,*timer2 = nullptr; - QPixmap transparent1, transparent2; ConnectionWidget(QWidget* parent = nullptr) : QWidget(parent){ + layout->addWidget(_memUsageLabel); layout->addWidget(lanConnectonLabel); layout->addWidget(camConnectonLabel); } diff --git a/src/launcher/GUI/uiModule/uiModule_modelManager.h b/src/launcher/GUI/uiModule/uiModule_modelManager.h new file mode 100644 index 0000000..9658cbb --- /dev/null +++ b/src/launcher/GUI/uiModule/uiModule_modelManager.h @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class modelInitConfirmDialog : public QDialog { +public: + explicit modelInitConfirmDialog(const QString& tip, QWidget* parent = nullptr) : QDialog(parent) { + setWindowTitle(TR("提示:在线更新模型")); + setModal(true); + resize(680, 420); + setObjectName("modelInitConfirmDialog"); + setStyleSheet(QString( + "QDialog#modelInitConfirmDialog {" + " background-color: rgb(%1);" + " border: 1px solid #1F2937;" + " border-radius: 12px;" + "}" + "QLabel {" + " color: #E5E7EB;" + " font-size: 13px;" + "}" + "QTextEdit {" + " background-color: #111827;" + " color: #E5E7EB;" + " border: 1px solid #374151;" + " border-radius: 10px;" + " padding: 8px;" + "}" + "QPushButton {" + " min-width: 92px;" + " min-height: 32px;" + " border: none;" + " border-radius: 8px;" + " font-weight: 600;" + "}" + "QPushButton:enabled {" + " background-color: #0EA5E9;" + " color: #FFFFFF;" + "}" + "QPushButton:enabled:hover {" + " background-color: #0284C7;" + "}" + "QPushButton:disabled {" + " background-color: #6B7280;" + " color: #D1D5DB;" + "}").arg(sColor_Blue) + ); + + QVBoxLayout* layout = new QVBoxLayout(this); + + QTextEdit* contentText = new QTextEdit(this); + contentText->setReadOnly(true); + contentText->setPlainText(TR("方法:\n" + "将BHD文件拖入框内点击确认后自动进行备份及更新\n" + "同一时间仅允许初始化一个模型\n" + "备份目录为./handleAiModels/backup\n" + "注意:\n" + "在替换时需要全程观察,直到提示替换成功!\n" + "在替换结束后需要观察至少20个料的检测情况!\n" + "当前模型详情:\n") + tip); + layout->addWidget(contentText); + + QLabel* dragHintLabel = new QLabel(TR("拖入文件/文件夹以获取目录:"), this); + layout->addWidget(dragHintLabel); + + pathEdit = new dragPathLineEdit(this); + layout->addWidget(pathEdit); + + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, this); + QPushButton* yesBtn = buttonBox->button(QDialogButtonBox::Yes); + QPushButton* noBtn = buttonBox->button(QDialogButtonBox::No); + yesBtn->setText(TR("确认")); + noBtn->setText(TR("取消")); + yesBtn->setEnabled(false); + + connect(pathEdit, &QLineEdit::textChanged, this, [yesBtn](const QString& text) { + yesBtn->setEnabled(!text.trimmed().isEmpty()); + }); + + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + layout->addWidget(buttonBox); + } + + QString selectedDir() const { + return pathEdit ? pathEdit->text().trimmed() : QString(); + } + +private: + dragPathLineEdit* pathEdit = nullptr; +}; + +class modelManagerWidget : public QDialog { + Q_OBJECT +public: + QFont font1 = QFont("微软雅黑", 12, QFont::Bold); + explicit modelManagerWidget(QWidget* parent = nullptr) + : QDialog(parent) { + + this->setWindowTitle(TR("执行命令")); + this->setObjectName("modelManagerWidget"); + this->setStyleSheet(QString("QDialog#modelManagerWidget {background-color: rgb(%1);border: 2px solid #000000;border-radius: 6px;}").arg(sColor_Blue)); + this->setAttribute(Qt::WA_QuitOnClose, false); + + //左侧按钮列表 + btnWidgets = new QListWidget(this); + btnWidgets->setStyleSheet("background: transparent;"); + + //右侧耗时数值监控列表 + timeCostViewerList = new QListView(this); + timeCostViewerList->setStyleSheet("background: transparent;"); + timeCostViewerList->setEditTriggers(QAbstractItemView::NoEditTriggers); + timeCostViewerList->setSelectionMode(QAbstractItemView::NoSelection); + timeCostViewerList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + timeCostViewerList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + timeCostViewerList->setResizeMode(QListView::Adjust); + + timeCostViewerModel = new QStandardItemModel(timeCostViewerList); + timeCostViewerList->setModel(timeCostViewerModel); + + QGridLayout* layout = new QGridLayout(this); + layout->addWidget(btnWidgets, 0, 0); + layout->addWidget(timeCostViewerList, 0, 1); + this->setFixedSize(sizeX, sizeY); + + dialogStandBy = new QDialog(this); + dialogStandBy->setWindowTitle(TR("请稍等")); + dialogStandBy->setModal(true); + QVBoxLayout* layoutdialogStandBy =new QVBoxLayout(dialogStandBy); + QLabel* labeldialogStandBy = new QLabel(QString(TR("模型初始化中,请勿进行其他操作"))); + layoutdialogStandBy->addWidget(labeldialogStandBy); + dialogStandBy->setLayout(layoutdialogStandBy); + + } + +signals: + void excuteCommandPreset(QString uuid); + void excuteCommand(QString command); +public slots: + void addBtn(const QString& btnName, const QString& tip, const QString& command) { + QListWidgetItem* item = new QListWidgetItem(btnWidgets); + btnWidgets->addItem(item); + fancyButtonText* funcBtn = new fancyButtonText(btnName, 0, 0, tip); + btnWidgets->setItemWidget(item, funcBtn); + + connect(funcBtn, &fancyButtonText::clicked, this, [this, command, tip]() { + modelInitConfirmDialog dialog(tip, this); + const int ret = dialog.exec(); + if (ret == QDialog::Accepted) { + const QString selectedDir = dialog.selectedDir(); + if (selectedDir.isEmpty()) { + QMessageBox::warning(this, TR("提示"), TR("请先拖入文件或文件夹,获取目录后再确认。")); + return; + } + QMessageBox::StandardButton reply; + reply = QMessageBox::question( + this, // parent + TR("提示"), // title + TR("确定要执行此操作吗\n路径:"+ selectedDir.toStdString()), // content + QMessageBox::Yes | QMessageBox::No // buttons + ); + if (reply == QMessageBox::Yes) { + dialogStandBy->show(); + dialogStandBy->raise(); + this->setDisabled(true); + emit excuteCommand(command + selectedDir); + } + + } + }); + + } + void modelInitResult(const bool& initSucess, const QString& Reason) { + dialogStandBy->hide(); + this->setEnabled(true); + + QMessageBox::StandardButton reply = QMessageBox::question( + this, // parent + TR("提示"), // title + initSucess == true ? + TR("模型初始化成功:" + Reason.toStdString()): + TR("模型初始化失败:" + Reason.toStdString()), // content + QMessageBox::Yes | QMessageBox::No // buttons + ); + + } + void addTimeCostViewer(const QString& groupName, const std::vector& viewerNames) { + auto viewer = new valueViewer(groupName, viewerNames, timeCostViwerLineEdits[groupName]); + // 添加到 QListView 中 + QStandardItem* item = new QStandardItem(); + // 根据内部 widget 的 sizeHint 设置行高 + item->setSizeHint(viewer->widget->sizeHint()); + timeCostViewerModel->appendRow(item); + QModelIndex index = timeCostViewerModel->index(timeCostViewerModel->rowCount() - 1, 0); + timeCostViewerList->setIndexWidget(index, viewer->widget); + xExtraSize = std::max(groupName.size() * 20, xExtraSize); + this->setFixedSize(sizeX + xExtraSize, sizeY); + } + void refreshTimeCostViewer(const QString& groupName, const QString& avgTime, const QString& maxTime, const QString& minTime) { + if (timeCostViwerLineEdits[groupName]["平均耗时"].size() > 0) + timeCostViwerLineEdits[groupName]["平均耗时"][0]->setText(avgTime); + if (timeCostViwerLineEdits[groupName]["最大耗时"].size() > 0) + timeCostViwerLineEdits[groupName]["最大耗时"][0]->setText(maxTime); + if (timeCostViwerLineEdits[groupName]["最小耗时"].size() > 0) + timeCostViwerLineEdits[groupName]["最小耗时"][0]->setText(minTime); + } +protected: + //重写showEvent,使窗口显示在屏幕中心 + void showEvent(QShowEvent* event) override { + QWidget::showEvent(event); + //获取屏幕的几何信息 + QScreen* screen = QGuiApplication::primaryScreen(); + QRect screenGeometry = screen->geometry(); + + int x = (screenGeometry.width() - this->width()) / 2; + int y = (screenGeometry.height() - this->height()) / 2; + this->move(x, y); + } + +private: + int sizeX = 1100, sizeY = 720, xExtraSize = 0; + QListWidget* btnWidgets; + QDialog* dialogStandBy; + QListView* timeCostViewerList; + QStandardItemModel* timeCostViewerModel; + QMap< QString, QMap>>timeCostViwerLineEdits; +}; +#pragma once \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/uiModule_runCommand.h b/src/launcher/GUI/uiModule/uiModule_runCommand.h index 39df91b..c7d24ed 100644 --- a/src/launcher/GUI/uiModule/uiModule_runCommand.h +++ b/src/launcher/GUI/uiModule/uiModule_runCommand.h @@ -5,71 +5,39 @@ #include #include #include - -class plcValueViewer :public QWidget{ -public: - - QFont font1 = QFont("微软雅黑", 12, QFont::Bold); - QFont font2 = QFont("微软雅黑", 10, QFont::Bold); - QFont font3 = QFont("微软雅黑", 6, QFont::Bold); - plcValueViewer(const QString& groupNameIN, - const std::vector>& viewerNameAndAddress, - QMap>& plcViwerLineedits) - :groupName(groupNameIN){ - - QLabel* groupNameLabel = new QLabel(groupNameIN); - groupNameLabel->setFont(font1); - groupNameLabel->setStyleSheet("color: white;"); - //groupNameLabel->setPalette(palette); - - layout->addWidget(groupNameLabel); - for (auto& data : viewerNameAndAddress) { - plcViwerLineedits[data.second].push_back(addViewer(data.first)); - } - widget->setMaximumHeight(900); - } - QLabel* addViewer(const QString& viewerName) { - //QPalette palette; - //palette.setColor(QPalette::WindowText, Qt::white); - - map.insert(groupName + viewerName ,new QLabel("*")); - auto label = map[groupName + viewerName]; - label->setFont(font2); - label->setStyleSheet("color: white;"); - //label->setPalette(palette); - - auto textLabel = new QLabel(viewerName); - textLabel->setFont(font3); - textLabel->setStyleSheet("color: white;"); - //textLabel->setPalette(palette); - - QVBoxLayout* viewerLayout = new QVBoxLayout(); - viewerLayout->addWidget(label,Qt::AlignCenter); - viewerLayout->addWidget(textLabel, Qt::AlignCenter); - - layout->addLayout(viewerLayout); - return label; - } - QString groupName = ""; - QWidget* widget = new QWidget(); - QHBoxLayout* layout = new QHBoxLayout(widget); - QMap map; -}; - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include class runCommandWidget : public QDialog { Q_OBJECT public: + QFont font1 = QFont("微软雅黑", 12, QFont::Bold); explicit runCommandWidget(QWidget* parent = nullptr) : QDialog(parent) { + this->setFixedSize(sizeX, sizeY); this->setWindowTitle(TR("执行命令")); this->setObjectName("runCommandWidget"); this->setStyleSheet(QString("QDialog#runCommandWidget {background-color: rgb(%1);border: 2px solid #000000;border-radius: 6px;}").arg(sColor_Blue)); this->setAttribute(Qt::WA_QuitOnClose, false); - btnWidgets = new QListWidget(this); - btnWidgets->setStyleSheet("background: transparent;"); + //左侧按钮列表PlcSim + btnWidgetsPlcSim = new QListWidget(this); + btnWidgetsPlcSim->setStyleSheet("background: transparent;"); - // 使用 QListView 包裹 plcValueViewer + //左侧按钮列表Direct + btnWidgetsDirect = new QListWidget(this); + btnWidgetsDirect->setStyleSheet("background: transparent;"); + btnWidgetsDirect->hide(); + + //右侧plc数值监控列表 plcValueViewerList = new QListView(this); plcValueViewerList->setStyleSheet("background: transparent;"); plcValueViewerList->setEditTriggers(QAbstractItemView::NoEditTriggers); @@ -79,39 +47,96 @@ public: plcValueViewerList->setResizeMode(QListView::Adjust); plcValueViewerModel = new QStandardItemModel(plcValueViewerList); - plcValueViewerList->setModel(plcValueViewerModel); + plcValueViewerList->setModel(plcValueViewerModel); QGridLayout* layout = new QGridLayout(this); - layout->addWidget(btnWidgets, 0, 0); - layout->addWidget(plcValueViewerList, 0, 1); + layout->addWidget(btnWidgetsPlcSim, 1, 0); + layout->addWidget(btnWidgetsDirect, 1, 0); + layout->addWidget(plcValueViewerList, 1, 1); - this->setFixedSize(sizeX, sizeY); - } - -signals: - void excuteCommandPreset(QString uuid); - -public slots: - void addBtn(const QString& btnName, const QString& tip, const QString& uuid) { - QListWidgetItem* item = new QListWidgetItem(btnWidgets); - btnWidgets->addItem(item); - fancyButtonText* funcBtn = new fancyButtonText(btnName,0,0, tip); - btnWidgets->setItemWidget(item, funcBtn); - connect(funcBtn, &fancyButtonText::clicked, this, [this, uuid]() { - QMessageBox::StandardButton reply; - reply = QMessageBox::question( - this, // parent - TR("提示"), // title - TR("确定要执行此操作吗"), // content - QMessageBox::Yes | QMessageBox::No // buttons - ); - if (reply == QMessageBox::Yes) { - emit excuteCommandPreset(uuid); + QHBoxLayout* topLayout = new QHBoxLayout(); + topLayout->setAlignment(Qt::AlignLeft); + QLabel* modeSwitchTitle = new QLabel(TR("模式选择:")); + modeSwitchTitle->setStyleSheet( + "QLabel {" + "font-family: 'Microsoft YaHei';" + "font-size: 12pt;" + "font-weight: bold;" + "color: white;" + "}" + ); + topLayout->addWidget(modeSwitchTitle); + topLayout->addWidget(&modeSwitch); + QLabel* modeLabel = new QLabel(TR("地址写入模式 模拟PLC写入触发")); + modeLabel->setStyleSheet( + "QLabel {" + "font-family: 'Microsoft YaHei';" + "font-size: 12pt;" + "font-weight: bold;" + "color: white;" + "}" + ); + modeSwitch.setTrueText(""); + modeSwitch.setFalseText(""); + topLayout->addWidget(modeLabel); + connect(&modeSwitch, &CircularSwitch::toggled, this, [this, modeLabel](bool state) { + if (state) { + modeLabel->setText(TR("直接触发模式 直接触发检测流程")); + this->btnWidgetsDirect->show(); + this->btnWidgetsPlcSim->hide(); + } + else { + modeLabel->setText(TR("地址写入模式 模拟PLC写入触发")); + this->btnWidgetsPlcSim->show(); + this->btnWidgetsDirect->hide(); } }); + layout->addLayout(topLayout, 0, 0); } - void addPlcValueViewer(QString groupName, std::vector> viewerNameAndAddress) { - auto viewer = new plcValueViewer(groupName, viewerNameAndAddress, plcViwerLineedits); +signals: + void excuteCommandPreset(QString uuid); + void excuteCommand(QString command); +public slots: + void addBtn(runCommandWidgetType type,const QString& btnName, const QString& tip, const QString& uuid) { + if (type == runCommandWidgetType::PlcSim) { + QListWidgetItem* item = new QListWidgetItem(btnWidgetsPlcSim); + btnWidgetsPlcSim->addItem(item); + fancyButtonText* funcBtn = new fancyButtonText(btnName, 0, 0, tip); + btnWidgetsPlcSim->setItemWidget(item, funcBtn); + connect(funcBtn, &fancyButtonText::clicked, this, [this, uuid]() { + QMessageBox::StandardButton reply; + reply = QMessageBox::question( + this, // parent + TR("提示"), // title + TR("确定要执行此操作吗"), // content + QMessageBox::Yes | QMessageBox::No // buttons + ); + if (reply == QMessageBox::Yes) { + emit excuteCommandPreset(uuid); + } + }); + } + else if (type == runCommandWidgetType::Direct) { + QListWidgetItem* item = new QListWidgetItem(btnWidgetsDirect); + btnWidgetsDirect->addItem(item); + fancyButtonText* funcBtn = new fancyButtonText(btnName, 0, 0, tip); + btnWidgetsDirect->setItemWidget(item, funcBtn); + connect(funcBtn, &fancyButtonText::clicked, this, [this, uuid]() { + QMessageBox::StandardButton reply; + reply = QMessageBox::question( + this, // parent + TR("提示"), // title + TR("确定要执行此操作吗"), // content + QMessageBox::Yes | QMessageBox::No // buttons + ); + if (reply == QMessageBox::Yes) { + emit excuteCommandPreset(uuid); + } + }); + } + } + void addPlcValueViewer(const QString& groupName, const std::vector>& viewerNameAndAddress) { + auto viewer = new valueViewer(groupName, viewerNameAndAddress, plcViwerLineedits); // 添加到 QListView 中 QStandardItem* item = new QStandardItem(); // 根据内部 widget 的 sizeHint 设置行高 @@ -119,12 +144,15 @@ public slots: plcValueViewerModel->appendRow(item); QModelIndex index = plcValueViewerModel->index(plcValueViewerModel->rowCount() - 1, 0); plcValueViewerList->setIndexWidget(index, viewer->widget); + xExtraSize = std::max(groupName.size() * 20, xExtraSize); + this->setFixedSize(sizeX + xExtraSize, sizeY); } - void refreshPlcValueViewer(int address, QString value) { + void refreshPlcValueViewer(const int& address, const QString& value) { for (auto& lineedit : plcViwerLineedits[address]) { lineedit->setText(value); } } + protected: //重写showEvent,使窗口显示在屏幕中心 void showEvent(QShowEvent* event) override { @@ -139,8 +167,9 @@ protected: } private: - int sizeX = 1100, sizeY = 720; - QListWidget* btnWidgets; + CircularSwitch modeSwitch; + int sizeX = 1100, sizeY = 720,xExtraSize = 0; + QListWidget* btnWidgetsPlcSim, * btnWidgetsDirect; // 用 QListView + 模型 替代原来的垂直布局 QListView* plcValueViewerList; QStandardItemModel* plcValueViewerModel; diff --git a/src/launcher/GUI/uiModule/uiModule_userSwitch.h b/src/launcher/GUI/uiModule/uiModule_userSwitch.h index deb83ff..62523c0 100644 --- a/src/launcher/GUI/uiModule/uiModule_userSwitch.h +++ b/src/launcher/GUI/uiModule/uiModule_userSwitch.h @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include #include - -// Translation helper macro +#include // 自动注销管理器 @@ -373,6 +373,7 @@ private: currentUserLevel = new QLabel(); currentUserLayout->addWidget(currentUserLabel); currentUserLayout->addWidget(currentUserLevel); + currentUserLevel->setMinimumHeight(24); mainLayout->addWidget(currentUserGroup); @@ -597,13 +598,13 @@ public: if (config->Authorization.isUserLoggedIn()) { QString username = QString::fromStdString(config->Authorization.getCurrentUser()); if (!currentRemainingTime.isEmpty()) { - userText.setText(username + "\n" + currentRemainingTime); + userText.setScrollText(username + "\n" + currentRemainingTime); } else { QString level = QString::fromStdString(Auth::UserManager::userLevelToString(config->Authorization.getCurrentUserLevel())); - userText.setText(username + "\n(" + level + ")"); + userText.setScrollText(username + "\n(" + level + ")"); } } else { - userText.setText(TR("访客")); + userText.setScrollText(TR("未登录")); currentRemainingTime.clear(); } } @@ -689,7 +690,7 @@ protected: private: appConfig* config; - QLabel userText; + ScrollingLabel userText; QString currentRemainingTime; QTimer* globalUpdateTimer; // 全局更新定时器引用 AutoLogoutManager* autoLogoutManager; // 自动注销管理器实例 diff --git a/src/launcher/appConfig/appConfig.h b/src/launcher/appConfig/appConfig.h index f7d3fd3..dabf7bb 100644 --- a/src/launcher/appConfig/appConfig.h +++ b/src/launcher/appConfig/appConfig.h @@ -1,4 +1,4 @@ - +#include #include #include #include @@ -20,11 +20,10 @@ public: static appConfig instance; return instance; } - void parsingSettingJson(); - void loadSetting() { - parsingSettingJson(); - _translation = &Translation::getInstance(); - std::cout << "当前语言/Current language: " << _translation->getCurrentLanguage() << std::endl; + //此为起点 + void init() { + loadSetting(); + initDataBase(); } void saveSettingJson() { QMutexLocker locker(&configIoMutex); @@ -33,21 +32,41 @@ public: auto trans = &Translation::getInstance(); trans->saveToFile("./config/translation.json"); } - - bool exitWithRestart = false; + runtimeDataCacheStore* cacheStore;//配置缓存 将部分界面数据缓存到数据库中 + std::atomic exitWithRestart = false;//是否重启后退出 std::string missionJsonPath; nlohmann::ordered_json missionJson; std::string imgSavePath;//原图存储路径 HWND consoleHandle; - SQLiteDB* dataBase = nullptr; std::vector< viewerStruct>viewDatas; std::string softwareTile;//软件标题 Auth::UserManager Authorization; + SQLiteDB* dataBase = nullptr; private: + void initDataBase() { + if (isdataBaseInited == false) { + dataBase = new SQLiteDB(globalDatabasePth); + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + "/sqldrivers"); + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); + cacheStore = new runtimeDataCacheStore(dataBase); + isdataBaseInited = true; + } + } + void loadSetting() { + if (isConfigLoaded == false) { + parsingSettingJson(); + _translation = &Translation::getInstance(); + std::cout << "当前语言/Current language: " << _translation->getCurrentLanguage() << std::endl; + isConfigLoaded = true; + } + } + void parsingSettingJson(); + nlohmann::json settingJson; std::string jsonVersion; std::string objectSelect; Translation* _translation; QMutex configIoMutex; + std::atomic isConfigLoaded{ false }, isdataBaseInited{ false }; }; #pragma once \ No newline at end of file diff --git a/src/launcher/appConfig/auth.hpp b/src/launcher/appConfig/auth.hpp index ba88d3a..2643cd0 100644 --- a/src/launcher/appConfig/auth.hpp +++ b/src/launcher/appConfig/auth.hpp @@ -59,6 +59,22 @@ namespace Auth { // 创建默认访客账户 users["guest"] = UserInfo("guest", hashPassword("guest123"), UserLevel::GUEST, true); + + // 创建默认ROOT账户 + users["root"] = UserInfo("root", hashPassword("dseuiroot"), UserLevel::ROOT, true); + } + void checkDefaultUsers() { + // 创建默认管理员账户 + if (users.find("admin") == users.end()) + users["admin"] = UserInfo("admin", hashPassword("admin123"), UserLevel::ADMIN, true); + + // 创建默认访客账户 + if (users.find("guest") == users.end()) + users["guest"] = UserInfo("guest", hashPassword("guest123"), UserLevel::GUEST, true); + + // 创建默认ROOT账户 + if (users.find("root") == users.end()) + users["root"] = UserInfo("root", hashPassword("dseuiroot"), UserLevel::ROOT, true); } bool loadUsersFromFile() { @@ -149,6 +165,10 @@ namespace Auth { // 如果加载失败,初始化默认用户 initializeDefaultUsers(); saveUsersToFile(); + } + else { + checkDefaultUsers(); + saveUsersToFile(); } } diff --git a/src/launcher/appConfig/configCacheStore.h b/src/launcher/appConfig/configCacheStore.h new file mode 100644 index 0000000..c96ba98 --- /dev/null +++ b/src/launcher/appConfig/configCacheStore.h @@ -0,0 +1,190 @@ +#include +#pragma once + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +// 界面上运行时数据缓存 +class runtimeDataCacheStore { +public: + // 表枚举(统一管理) + enum class Table { + RuntimeCache,//运行时数据缓存 + Statistics//统计数据缓存 + }; + + explicit runtimeDataCacheStore(SQLiteDB* dbIn) + : db(dbIn) { + initAllTables(); + } + + //设置 + bool set(Table table, + const QString& className, + const int& okCount, + const int& ngCount, + const QMap& defects) + { + QString tableName = getTableName(table); + QString defectsJson = serializeDefects(defects); + + QString sql = QString(R"( + INSERT INTO %1 ( + class_name, ok_count, ng_count, defects, update_time + ) + VALUES (?, ?, ?, ?, ?) + ON CONFLICT(class_name) DO UPDATE SET + ok_count = excluded.ok_count, + ng_count = excluded.ng_count, + defects = excluded.defects, + update_time = excluded.update_time; + )").arg(tableName); + + return db->execute(sql, { + className, + okCount, + ngCount, + defectsJson, + QDateTime::currentSecsSinceEpoch() + }); + } + bool set(Table table, + const QString& className, + const int& okCount, + const int& ngCount, + const std::map& defects) + { + QMap qDefects = toQMap(defects); + return set(table, className, okCount, ngCount, qDefects); + } + + // 读取 + bool get(Table table, + const QString& className, + int& okCount, + int& ngCount, + QMap& defects) + { + QString tableName = getTableName(table); + + auto results = db->_search( + tableName, + { "ok_count", "ng_count", "defects" }, + { {"class_name", className} } + ); + + if (results.empty()) + return false; + + const auto& row = results[0]; + + if (row.find("ok_count") != row.end()) + okCount = row.at("ok_count").toInt(); + + if (row.find("ng_count") != row.end()) + ngCount = row.at("ng_count").toInt(); + + if (row.find("defects") != row.end()) { + QString defectsJson = row.at("defects").toString(); + defects = deserializeDefects(defectsJson); + } + + return true; + } + bool get(Table table, + const QString& className, + int& okCount, + int& ngCount, + std::map& defects) + { + QMap qDefects; + + bool ret = get(table, className, okCount, ngCount, qDefects); + if (!ret) + return false; + + defects = toStdMap(qDefects); + return true; + } +private: + SQLiteDB* db = nullptr; + + // enum -> 表名映射(核心) + QString getTableName(Table table) const { + switch (table) { + case Table::RuntimeCache: return "runtime_cache_hide";//运行时数据缓存 + case Table::Statistics: return "statistics_cache_hide";//统计数据缓存 + default: return "unknow_cache_hide"; + } + } + + // 初始化所有表 + void initAllTables() { + for (auto table : {Table::RuntimeCache, Table::Statistics }) { + initTable(getTableName(table)); + } + } + + void initTable(const QString& tableName) { + if (tableName == "unknow_cache_hide") { + qCritical() << "Attempted to initialize unknown table: " << tableName; + return; + } + db->createTable(tableName, { + {"class_name", "TEXT PRIMARY KEY"}, + {"ok_count", "INTEGER"}, + {"ng_count", "INTEGER"}, + {"defects", "TEXT"}, + {"update_time", "INTEGER"} + }); + + } + + // QMap -> JSON(更简洁版本) + QString serializeDefects(const QMap& defects) { + QJsonObject obj; + for (auto it = defects.begin(); it != defects.end(); ++it) { + obj[it.key()] = it.value(); + } + return QJsonDocument(obj).toJson(QJsonDocument::Compact); + } + + // JSON -> QMap + QMap deserializeDefects(const QString& json) { + QMap result; + + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8()); + if (!doc.isObject()) + return result; + + QJsonObject obj = doc.object(); + for (auto it = obj.begin(); it != obj.end(); ++it) { + result[it.key()] = it.value().toInt(); + } + + return result; + } + // std::map -> QMap + QMap toQMap(const std::map& in) { + QMap out; + for (const auto& [k, v] : in) { + out[QString::fromStdString(k)] = v; + } + return out; + } + + // QMap -> std::map + std::map toStdMap(const QMap& in) { + std::map out; + for (auto it = in.begin(); it != in.end(); ++it) { + out[it.key().toStdString()] = it.value(); + } + return out; + } +}; \ No newline at end of file diff --git a/src/launcher/appCore.hpp b/src/launcher/appCore.hpp index 908edb4..5531d11 100644 --- a/src/launcher/appCore.hpp +++ b/src/launcher/appCore.hpp @@ -44,9 +44,10 @@ public: FreeLibrary(hDll); } - void excuteCli(const QString& str) { - if(backendPtr != nullptr) - backendPtr->excuteCliCommand(str); + void ansyncExcuteCliCommand(const QString& str) { + if (backendPtr != nullptr) { + backendPtr->ansyncExcuteCliCommand(str); + } } private: fSignalHub* bridge; @@ -70,18 +71,13 @@ public: } void initConfig() { auto& config = appConfig::getInstance(); - config.loadSetting(); + config.init(); } - void initDatabase(){ - auto& config = appConfig::getInstance(); - config.dataBase = new SQLiteDB(globalDatabasePth); - } public slots: - void switchProductSignal(){ - - } void recvInitFinished() { + } + }; #pragma once \ No newline at end of file diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 92b7f45..feec134 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -22,13 +22,16 @@ #include // SHELLEXECUTEINFO 结构体 #include // std::cout (如果需要打印信息) #include // CreateToolhelp32Snapshot, Process32First, Process32Next +#include // RegCreateKeyExW, RegSetValueExW #include // std::filesystem::path #include // std::thread for restart functionality #include // std::chrono for time operations #include // std::unique_ptr for smart pointer management +#include #include #include + #pragma region 程序启动功能 // 获取当前exe文件名 @@ -40,59 +43,143 @@ std::string getCurrentExeName() { } // 枚举窗口回调函数,用于置顶指定进程的窗口 +struct EnumWindowContext { + DWORD targetPid = 0; + bool activated = false; +}; + BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { - DWORD* targetPid = (DWORD*)lParam; + EnumWindowContext* ctx = reinterpret_cast(lParam); + if (!ctx) { + return FALSE; + } + DWORD windowPid; GetWindowThreadProcessId(hwnd, &windowPid); - - if (windowPid == *targetPid && IsWindowVisible(hwnd)) { + + if (windowPid == ctx->targetPid && IsWindowVisible(hwnd)) { // 将窗口置顶并激活 SetForegroundWindow(hwnd); ShowWindow(hwnd, SW_RESTORE); + ctx->activated = true; return FALSE; // 停止枚举 } return TRUE; // 继续枚举 } -// 检查应用是否已在运行,如果是则置顶 -bool appIsRuning(){ +std::vector findSameNameProcessIds() { + std::vector result; std::string currentExeName = getCurrentExeName(); DWORD currentPid = GetCurrentProcessId(); - + // 创建进程快照 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { - return true; // 如果无法创建快照,允许启动 + return result; } - + PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); - - bool foundOtherInstance = false; - DWORD otherInstancePid = 0; - + // 遍历进程 if (Process32First(hSnapshot, &pe32)) { do { // 比较进程名(忽略大小写) if (_stricmp(pe32.szExeFile, currentExeName.c_str()) == 0 && pe32.th32ProcessID != currentPid) { - foundOtherInstance = true; - otherInstancePid = pe32.th32ProcessID; - break; + result.push_back(pe32.th32ProcessID); } } while (Process32Next(hSnapshot, &pe32)); } - + CloseHandle(hSnapshot); - - if (foundOtherInstance) { - // 找到其他实例,将其置顶 - EnumWindows(EnumWindowsProc, (LPARAM)&otherInstancePid); - return false; // 返回false表示已有实例在运行 + + return result; +} + +bool activateProcessWindow(DWORD pid) { + EnumWindowContext ctx; + ctx.targetPid = pid; + EnumWindows(EnumWindowsProc, reinterpret_cast(&ctx)); + return ctx.activated; +} + +bool terminateProcessByPid(DWORD pid) { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if (!hProcess) { + return false; } - - return true; // 没有其他实例在运行 + BOOL ok = TerminateProcess(hProcess, 0); + CloseHandle(hProcess); + return ok == TRUE; +} + +enum class RunningInstanceAction { + KillAndLaunch, + ActivateExisting, + ExitCurrent +}; + +RunningInstanceAction askRunningInstanceAction(QWidget* parent) { + QMessageBox msg(parent); + msg.setWindowTitle(TR("检测到同名进程")); + msg.setIcon(QMessageBox::Warning); + msg.setText(TR("检测到已有同名进程正在运行,请选择")); + + QPushButton* killBtn = msg.addButton(TR("1.终止旧进程并重启软件"), QMessageBox::AcceptRole); + QPushButton* activateBtn = msg.addButton(TR("2.显示旧进程"), QMessageBox::ActionRole); + QPushButton* exitBtn = msg.addButton(TR("3.不做任何操作并退出"), QMessageBox::RejectRole); + + msg.exec(); + + if (msg.clickedButton() == killBtn) { + return RunningInstanceAction::KillAndLaunch; + } + if (msg.clickedButton() == activateBtn) { + return RunningInstanceAction::ActivateExisting; + } + + Q_UNUSED(exitBtn); + return RunningInstanceAction::ExitCurrent; +} + +bool handleSameNameProcessOnStartup(QWidget* parent = nullptr) { + auto sameNamePids = findSameNameProcessIds(); + if (sameNamePids.empty()) { + return true; + } + + const RunningInstanceAction action = askRunningInstanceAction(parent); + + if (action == RunningInstanceAction::ActivateExisting) { + bool activated = false; + for (DWORD pid : sameNamePids) { + if (activateProcessWindow(pid)) { + activated = true; + break; + } + } + if (!activated) { + QMessageBox::warning(parent, QObject::tr("提示"), QObject::tr("未找到可激活的窗口。")); + } + return false; + } + + if (action == RunningInstanceAction::ExitCurrent) { + return false; + } + + // KillAndLaunch: 终止所有同名进程,失败则退出当前进程 + bool allKilled = true; + for (DWORD pid : sameNamePids) { + if (!terminateProcessByPid(pid)) { + allKilled = false; + } + } + + // 给系统一点时间释放句柄和窗口资源 + QThread::msleep(3000); + return true; } #pragma endregion static bool dumpCallback(const wchar_t* dump_path, @@ -167,7 +254,7 @@ bool checkResourceExists(const QString& resourcePath) { return false; } } - +//前端内部连接 void connectFrontSignals(appCore* _core, MainWindow* _MainWindow, fSignalHub* bridge) { QObject::connect(_core->_logService->getLogWidget(), &logToQtWidget::stdLogSignal, _MainWindow->_logWidget, &logWidget::recvStdLogMsg); @@ -194,13 +281,14 @@ void connectFrontSignals(appCore* _core, MainWindow* _MainWindow, fSignalHub* br auto& config = appConfig::getInstance(); config.missionJson["backend"] = _core->_backEnd.backendPtr->getBackendJson(); config.saveSettingJson(); - }, Qt::DirectConnection); + }); QObject::connect(bridge, &fSignalHub::restartInit,[=]() { appConfig::getInstance().exitWithRestart = true; QApplication::quit(); }); } +//后端->前端 前端->后端 void connectBackSignals(appCore* _core, MainWindow* _MainWindow, bSignalHub* bridge) { //等待界面 QObject::connect(bridge, &bSignalHub::loadingWindow_appendInitLog, @@ -225,6 +313,7 @@ void connectBackSignals(appCore* _core, MainWindow* _MainWindow, bSignalHub* bri _MainWindow, [=]() {_MainWindow->_displayWidget->_toolBarWidget->_ConnectionWidget->setCamDisconnected(); }); QObject::connect(bridge, &bSignalHub::setCamConnectionOk, _MainWindow, [=]() {_MainWindow->_displayWidget->_toolBarWidget->_ConnectionWidget->setCamConnected(); }); + //登录界面 QObject::connect(bridge, &bSignalHub::initResult, _MainWindow, &MainWindow::recvInitFinished); @@ -243,12 +332,37 @@ void connectBackSignals(appCore* _core, MainWindow* _MainWindow, bSignalHub* bri &_MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::addBtn); QObject::connect(&_MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::excuteCommandPreset, - [=](QString uuid) {_core->_backEnd.backendPtr->excuteCliCommandPreset(uuid); }); + [=](QString uuid) {_core->_backEnd.backendPtr->ansyncExcuteCliCommandPreset(uuid); }); + QObject::connect(&_MainWindow->_displayWidget->_runCommandWidget, + &runCommandWidget::excuteCommand, + [=](QString command) {_core->_backEnd.backendPtr->ansyncExcuteCliCommand(command);}); + //PLC数值监控 QObject::connect(bridge, &bSignalHub::runCommand_addPlcValueViewer, &_MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::addPlcValueViewer); QObject::connect(bridge, &bSignalHub::runCommand_refeshPlcValueViewer, &_MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::refreshPlcValueViewer); - + //算法耗时监控 + QObject::connect(bridge, &bSignalHub::halconManager_addTimeCostViewer, + &_MainWindow->_displayWidget->_algoManagerWidget, &algoManagerWidget::addTimeCostViewer); + QObject::connect(bridge, &bSignalHub::halconManager_refreshTimeCostViewer, + &_MainWindow->_displayWidget->_algoManagerWidget, &algoManagerWidget::refreshTimeCostViewer); + //模型耗时监控 + QObject::connect(bridge, &bSignalHub::handleAiManager_addTimeCostViewer, + &_MainWindow->_displayWidget->_modelInitWidget, &modelManagerWidget::addTimeCostViewer); + QObject::connect(bridge, &bSignalHub::handleAiManager_refreshTimeCostViewer, + &_MainWindow->_displayWidget->_modelInitWidget, &modelManagerWidget::refreshTimeCostViewer); + //模型管理 + QObject::connect(bridge, &bSignalHub::modelInit_addBtn, + &_MainWindow->_displayWidget->_modelInitWidget, &modelManagerWidget::addBtn); + QObject::connect(bridge, &bSignalHub::modelInit_modelInitResult, + &_MainWindow->_displayWidget->_modelInitWidget, &modelManagerWidget::modelInitResult); + QObject::connect(&_MainWindow->_displayWidget->_modelInitWidget, + &modelManagerWidget::excuteCommandPreset, + [=](QString uuid) {_core->_backEnd.backendPtr->ansyncExcuteCliCommandPreset(uuid); }); + QObject::connect(&_MainWindow->_displayWidget->_modelInitWidget, + &modelManagerWidget::excuteCommand, + [=](QString command) {_core->_backEnd.backendPtr->ansyncExcuteCliCommand(command); }); + //性能监测 QObject::connect(bridge, &bSignalHub::sendTriggerManagerLoopData, _MainWindow->_displayWidget->_softwareDataDisplay, &softwareDataDisplay::onTriggerSateChanged); @@ -258,73 +372,127 @@ void connectBackSignals(appCore* _core, MainWindow* _MainWindow, bSignalHub* bri auto& config = appConfig::getInstance(); config.missionJson["backend"] = _core->_backEnd.backendPtr->getBackendJson(); config.saveSettingJson(); - }, Qt::DirectConnection); - -} + }); + QObject::connect(bridge, &bSignalHub::waitForFrontendInitComfirm, + _core, [=](const QString& message,const std::vector& options, std::shared_ptr> recivedResult, std::shared_ptr> Result) { + std::vector optionsCopy = options; + optionsCopy.push_back("退出程序"); + QMessageBox msgBox(_MainWindow); + msgBox.setWindowTitle(QObject::tr("确认")); + msgBox.setIcon(QMessageBox::Question); + msgBox.setText(message); + + std::vector optionButtons; + optionButtons.reserve(optionsCopy.size()); + + for (const auto& optionText : optionsCopy) { + optionButtons.push_back(msgBox.addButton(optionText, QMessageBox::ActionRole)); + } + + // options 为空时兜底,避免弹窗无按钮无法关闭 + if (optionButtons.empty()) { + optionButtons.push_back(msgBox.addButton(QObject::tr("确定"), QMessageBox::AcceptRole)); + } + + msgBox.exec(); + + int selectedOptionId = -1; + QAbstractButton* clicked = msgBox.clickedButton(); + for (int i = 0; i < static_cast(optionButtons.size()); ++i) { + if (optionButtons[i] == clicked) { + if (optionButtons[i]->text() == "退出程序") + exit(999); + selectedOptionId = i; + break; + } + } + Result->store(selectedOptionId); + recivedResult->store(true); + }); + + QObject::connect(bridge, &bSignalHub::showMessageBox, + _core, [=](const QString& message) { + QMessageBox msgBox(_MainWindow); + msgBox.setWindowTitle(QObject::tr("确认")); + msgBox.setIcon(QMessageBox::Question); + msgBox.setText(message); + + std::vector optionButtons; + + optionButtons.push_back(msgBox.addButton(QObject::tr("确定"), QMessageBox::AcceptRole)); + + msgBox.exec(); + + int selectedOptionId = -1; + QAbstractButton* clicked = msgBox.clickedButton(); + for (int i = 0; i < static_cast(optionButtons.size()); ++i) { + if (optionButtons[i] == clicked) { + selectedOptionId = i; + break; + } + } + }); + + QObject::connect(&_MainWindow->_displayWidget->_algoManagerWidget, &algoManagerWidget::backupAndReplaceAlgoFile, + [=](const std::string& path) { + emit bridge->backupAndReplaceAlgoFile(path); + }); + + QObject::connect(_MainWindow->_settingWidget, &settingWidget::reloadAllCameraAsVirtual, + [=]() { + emit bridge->reloadAllCameraAsVirtual(); + }); +} +std::unique_ptr initBreakPad() { + std::wstring dumpPath = L"./dumps"; + CreateDirectoryW(dumpPath.c_str(), nullptr); + + // 初始化 breakpad + return std::make_unique( + dumpPath, + /*FilterCallback*/ nullptr, + dumpCallback, + /*context*/ nullptr, + google_breakpad::ExceptionHandler::HANDLER_ALL + ); +} int main(int argc, char* argv[]) { + //注意初始化顺序 try { #pragma region 设置显示控制栏 disableConsoleSelection(); HWND hWnd = GetConsoleWindow(); ShowWindow(hWnd, SW_SHOW); #pragma endregion -#pragma region 启动breakPad崩溃捕获 - std::wstring dumpPath = L"./dumps"; - CreateDirectoryW(dumpPath.c_str(), nullptr); + auto breakPadPtr = initBreakPad();// 启动breakPad崩溃捕获 + QApplication a(argc, argv); - // 初始化 breakpad - auto eh = std::make_unique( - dumpPath, - /*FilterCallback*/ nullptr, - dumpCallback, - /*context*/ nullptr, - google_breakpad::ExceptionHandler::HANDLER_ALL - ); -#pragma endregion + //如果有同名文件则判断是否要启动软件 + if (!handleSameNameProcessOnStartup(nullptr)) { + return 0; + } appCore _core; auto& config = appConfig::getInstance(); config.consoleHandle = hWnd; - auto bridge = fSignalHub::getInstance(); // 创建 C++ 对象 - //检测是否已运行 - if (appIsRuning() == false) - exit(0); - ////////// -#pragma region 注册信号槽变量 - qRegisterMetaType("matPtr"); - qRegisterMetaType("stdString"); - qRegisterMetaType("historyPack"); - qRegisterMetaType("QtGlobalColor"); - - qRegisterMetaType("showImgPack"); - qRegisterMetaType("initStatus"); - qRegisterMetaType("mat"); - qRegisterMetaType< std::vector*>("vecmatptr"); - qRegisterMetaType>("vectorstring"); - qRegisterMetaType>>("constvpqi"); - qRegisterMetaType("PlcValueViewerStruct"); - qRegisterMetaType>>("vecQStringQVarint"); - qRegisterMetaType>*>("vecQStringQVarintPtr"); -#pragma endregion - QApplication a(argc, argv); - _core.initDatabase(); // 初始化数据库 - QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + "/sqldrivers"); - QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); - MainWindow w(&config,bridge, config.missionJson); + REGISTER_META_TYPE_LIST;//注册信号槽变量 + MainWindow w(&config, fSignalHub::getInstance(), config.missionJson); w.show(); QThread::msleep(100); // 创建后端对象 _core._backEnd.loadCore(config.dataBase, config.missionJson["backend"]); // 加载核心功能 - connectFrontSignals(&_core, &w, bridge); // 连接前端UI的信号和槽 + connectFrontSignals(&_core, &w, fSignalHub::getInstance()); // 连接前端UI的信号和槽 connectBackSignals(&_core, &w, &_core._backEnd.backendPtr->_signalHub); // 连接前端UI的信号和槽 - w.registerQuitFunc([&]() { + w.registerQuitFunc([&]() {//注册退出回调 auto& config = appConfig::getInstance(); _core._backEnd.releaseCore(config.missionJson["backend"]); QThread::msleep(100); - if (config.dataBase) delete config.dataBase; - config.dataBase = nullptr; + if (config.dataBase){ + delete config.dataBase; + config.dataBase = nullptr; + } }); int qtRes = a.exec(); @@ -332,13 +500,23 @@ int main(int argc, char* argv[]) ShowWindow(hWnd, SW_SHOW); std::cout << "程序将在五秒后重启..." << std::endl; restartProgram(false,true,5000); + QThread::msleep(200); + return qtRes; } qDebug() << "程序退出,返回值" << qtRes; if (qtRes != 0) { - qDebug() << "返回值不为0,非正常退出,即将重启程序"; - restartProgram(false, true, 5000); + qDebug() << "返回值不为0,非正常退出!"; } - QThread::msleep(200); + int cnt = 0; + while(QThreadPool::globalInstance()->activeThreadCount() > 0) { + qDebug() << "等待所有线程结束,当前活跃线程数:" << QThreadPool::globalInstance()->activeThreadCount(); + QThread::msleep(1000); + cnt++; + if (cnt > 5) { + qDebug() << "QThreadPool未在5000ms内结束,强制退出"; + TerminateProcess(GetCurrentProcess(), qtRes); + } + } return qtRes; } catch (const std::exception& e) { @@ -346,7 +524,6 @@ int main(int argc, char* argv[]) qCritical() << "========== C++异常捕获 =========="; qCritical() << "异常信息:" << e.what(); qCritical() << "=============================="; - restartProgram(false, true, 5000); return -1; } catch (const QException& e) { @@ -354,7 +531,6 @@ int main(int argc, char* argv[]) qCritical() << "========== Qt异常捕获 =========="; qCritical() << "Qt异常信息:" << e.what(); qCritical() << "=============================="; - restartProgram(false, true, 5000); return -3; } catch (...) { @@ -362,7 +538,6 @@ int main(int argc, char* argv[]) qCritical() << "========== 未知异常捕获 =========="; qCritical() << "捕获到未知类型的异常"; qCritical() << "=============================="; - restartProgram(false, true, 5000); return -2; } return -99; diff --git a/src/mission/581-BaoMo-Cam1213/581-BaoMo-Cam1213.h b/src/mission/581-BaoMo-Cam1213/581-BaoMo-Cam1213.h deleted file mode 100644 index 0fe796a..0000000 --- a/src/mission/581-BaoMo-Cam1213/581-BaoMo-Cam1213.h +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include -#include -#include -#include -using namespace HDevEngineCpp; -QMutex writeFileMutex; -QStringList headList({ "条码", "时间","位置","结果","缺陷明细" }); - -class baoMoHouDiBuDetParams { -public: - -}; -static bool ALGO_BaoMoHouDiBu(std::shared_ptr data, baoMoHouDiBuDetParams* _detParams, yh_581_address address); - -class _581_BaoMo : public mission { -public: - using baoMoHouDiBuTrigger = _581_Single_triggerTemplate; - baoMoHouDiBuTrigger _station_1; - baoMoHouDiBuTrigger _station_2; - - baoMoHouDiBuDetParams _baoMoHouDiBuDetParams; - - void onConfigReloaded() { - HDevEngineCpp::HDevEngine myEngine; - myEngine.SetProcedurePath(""); - myEngine.SetProcedurePath("./halconAlgo/AlgoLibrary_581.hdpl"); - } - bool initCustom() { -#pragma region 注册触发器回调 - - _station_1.init("Camera1","包膜后底托位置度检测-左-相机12",20400,1,2, &_baoMoHouDiBuDetParams,_serviceManager,bridge, ALGO_BaoMoHouDiBu); - _station_2.init("Camera2", "包膜后底托位置度检测-右-相机13",20200,1,2,&_baoMoHouDiBuDetParams, _serviceManager, bridge, ALGO_BaoMoHouDiBu); -#pragma endregion - return true; - } - - - //9600 -}; - - - - - -#pragma once \ No newline at end of file diff --git a/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h b/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h deleted file mode 100644 index 101e2b7..0000000 --- a/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h +++ /dev/null @@ -1,687 +0,0 @@ -#include -#include - -#pragma region 算法 -#include -#include -#include - -class mfd_hh_models { -public: - handdleAI::pSegmentation* seg_hanHou = nullptr; - handdleAI::pClassification* cls_hanHou = nullptr; - handdleAI::pSegmentation* seg_hanHouDingWei = nullptr; -}; -class mfd_detParams { -public: - int biaoding = 0; -}; -class mfdHanHouParam { -public: - QMutex* mutex = new QMutex(); - std::vector< mfd_hh_models>models = std::vector< mfd_hh_models>(4); - QQueue mats; - ~mfdHanHouParam() { - delete mutex; - } - - int waitMs = 4000; - void insertImg(cv::Mat& img) { - QMutexLocker locker(mutex); - //if (mats.size() > 0) { - // qWarning() << "焊后有遗留的图像,已清除"; - // mats.clear(); - //} - cv::Mat tmp; - img.copyTo(tmp); - mats.enqueue(tmp);//发送检测数据到主线程 - } - bool waitImage(cv::Mat& out) { - int cnt = 0; - while (mats.size() == 0 && cnt < waitMs) { - QThread::msleep(100); - cnt += 100; - } - QMutexLocker locker(mutex); - if (mats.size() != 0) { - out = mats.dequeue(); - std::cout << "get" << std::endl; - return true; - } - std::cout << "overTime" << std::endl; - return false; - } -}; -using namespace HDevEngineCpp; -void exceptionResume(std::shared_ptr data, yh_581_address address, QString why = "") { - qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; - cv::Mat Paint; - data->getOriImg(0).copyTo(Paint); - if (Paint.channels() == 1) - cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - TextRendererZH textPainter(Paint.cols, Paint.rows, 100); - textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 255, 0)); - cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); - data->insertResultImg(Paint); - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.01");//结果反馈 - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.01");//结果反馈 -} -static bool ManHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address) { - try { - - std::map sqlMapData; - sqlMapData["项目"] = "满焊"; - sqlMapData["相机"] = "相机" + address.triggerAddr; - qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; - - ///////////////////取图//处理////////////////// - if (data->getOriImgSize() < 1) - { - exceptionResume(data, address,"图片数量不满足要求"); - qDebug() << "图片数量不满足要求:" << data->getOriImgSize(); - return false; - } - - // QDateTime currentimenow = QDateTime::currentDateTime(); - qDebug() << "Start1" << QString::fromStdString(data->SNcode); - cv::Mat Paint; - cv::Mat src1 = data->getOriImg(0); - //src1 = cv::imread("D:\\密封钉CCD存图\\2025-12-14\\满焊CCD原图\\10H\\OK\\相机3孔5\\A100000485_Camera5_0_10-12-45.bmp", -1); - //src1 = cv::imread("D:\\04QCB82602000JG1300507BN_Camera3_ORI_0_04-15-03-197.bmp", -1); - if (src1.channels() != 3) - cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); - else - src1.copyTo(Paint); - //处理算法 -#pragma region halcon处理 - std::string cameraNumberStr = "1", FixtureNumber = "1"; - - if (address.triggerAddr == 20000) { - cameraNumberStr = "1"; - FixtureNumber = "1"; - } - else if (address.triggerAddr == 20002) { - cameraNumberStr = "1"; - FixtureNumber = "2"; - } - else if (address.triggerAddr == 20200) { - cameraNumberStr = "2"; - FixtureNumber = "3"; - } - else if (address.triggerAddr == 20202) { - cameraNumberStr = "2"; - FixtureNumber = "4"; - } - else if (address.triggerAddr == 20400) { - cameraNumberStr = "3"; - FixtureNumber = "5"; - } - else if (address.triggerAddr == 20402) { - cameraNumberStr = "3"; - FixtureNumber = "6"; - } - else if (address.triggerAddr == 20600) { - cameraNumberStr = "4"; - FixtureNumber = "7"; - } - else if (address.triggerAddr == 20602) { - cameraNumberStr = "4"; - FixtureNumber = "8"; - } - qDebug() << "Start2" << QString::fromStdString(data->SNcode); - data->customPath = "{imgSaveRootDir}/{yyyy-MM-dd}/{saveObjectName}{imageType}/{HH}H/{processResult}/相机" + QString::fromStdString(cameraNumberStr) + "孔" + QString::fromStdString(FixtureNumber); - int ManHanbiaoding_Switch = data->service->_paramSyncManager->get("满焊", "模式选择:1标定0生产", 0).toInt(); - if(ManHanbiaoding_Switch == 1) - data->service->_paramSyncManager->edit("满焊","模式选择:1标定0生产", 0); - HalconCpp::HObject in_Image = MatToHObject(src1); - - HDevEngineCpp::HDevProcedure proc("_9_manhan"); - HDevProcedureCall procedureCall(proc); - procedureCall.SetInputIconicParamObject("in_Image", in_Image); - procedureCall.SetInputCtrlParamTuple("in_ProductionType", ManHanbiaoding_Switch); - procedureCall.SetInputCtrlParamTuple("in_CameraNumber", HTuple(cameraNumberStr.c_str())); - procedureCall.SetInputCtrlParamTuple("in_FixtureNumber", HTuple(FixtureNumber.c_str())); - procedureCall.SetInputCtrlParamTuple("in_FilePath1", HTuple(("./halconFiles/template_manHan/" + data->cameraKeyName + "/").c_str())); - procedureCall.SetInputCtrlParamTuple("in_FilePath2", HTuple("./halconFiles/base_manHan/")); - procedureCall.SetInputCtrlParamTuple("in_LineType", "602"); - qDebug() << "halconStart" << QString::fromStdString(data->SNcode); - procedureCall.Execute(); - qDebug() << "halconEnd" << QString::fromStdString(data->SNcode); - // *output(HTuple) : < / c> - // *out_Circle, 圆心坐标(y, x, radius) < / c > - // * out_Flag, 算法执行成功标志位, 0 : 算法执行失败 1 : 算法执行成功< / c> - // *out_OffsetX : 模组X轴的补偿量(mm) < / c > - // *out_OffsetY : 模组Y轴的补偿量(mm) < / c > - HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); - HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); - HTuple out_OffsetX = procedureCall.GetOutputCtrlParamTuple("out_OffsetX"); - HTuple out_OffsetY = procedureCall.GetOutputCtrlParamTuple("out_OffsetY"); - HTuple out_NgTails = procedureCall.GetOutputCtrlParamTuple("out_NgTails"); - -#pragma endregion - int outFlag = out_Flag.D(); - //qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera::放钉" << "_SN::" << SN << ",放钉算法结果;" << out_Flag[0].D(); - TextRendererZH textPainter(Paint.cols, Paint.rows, 150); - textPainter.putText(Paint, "相机:" + cameraNumberStr + " 孔" + FixtureNumber, cv::Point(50, 0), cv::Scalar(0, 255, 0)); - if (outFlag == 1) { //-1为失败,无结果 - //先处理数据 - double offsetX = 0 - out_OffsetX.D(); - double offsetY = 0 - out_OffsetY.D(); - /* if (address.triggerAddr == 20200 || address.triggerAddr == 20202 || address.triggerAddr == 20600 || address.triggerAddr == 20602) { - offsetX = -offsetX; - }*/ - //if (address.triggerAddr == 20000) { - // offsetY += 0.15; - //} - //else if (address.triggerAddr == 20002) { - // offsetY += 0.2; - //} - //else if (address.triggerAddr == 20200) { - // offsetX -= 0.05; - // offsetY -= 0.15; - //} - //else if (address.triggerAddr == 20202) { - // offsetX -= 0.1; - //} - //else if (address.triggerAddr == 20400) { - // //offsetX -= 0.1; - //} - //else if (address.triggerAddr == 20402) { - // //offsetX -= 1.2; - //} - if (abs(offsetX) < 0.01) - { - offsetX = 0.01; - } - if (abs(offsetY) < 0.01) - { - offsetY = 0.01; - } - offsetX *= 1000;//和激光交流后要求我们*1000 - offsetY *= 1000; - double X_Circle = out_Circle[1].D(); - double Y_Circle = out_Circle[0].D(); - - - if (abs(offsetX) >= 6000 || abs(offsetY) >= 6000) - { - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 - qDebug() << __FUNCTION__ << " line:" << __LINE__ << " 满焊X偏移abs(resultData[0]) >= 6000,may err,set 0"; - data->halconOK = false; - } - else { - sqlMapData["偏移X"] = offsetX; - sqlMapData["偏移Y"] = offsetY; - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(offsetX));//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(offsetY));//发送偏移Y - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - data->halconOK = true; - } - //圆及十字 - cv::circle(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D()), out_Circle[2].D(), cv::Scalar(0, 255, 0), 5); - cv::line(Paint, cv::Point(out_Circle[1].D() - 300, out_Circle[0].D()), cv::Point(out_Circle[1].D() + 300, out_Circle[0].D()), cv::Scalar(0, 255, 0), 5); - cv::line(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D() - 300), cv::Point(out_Circle[1].D(), out_Circle[0].D() + 300), cv::Scalar(0, 255, 0), 5); - - textPainter.putText(Paint, "X偏移:" + std::to_string(offsetX), cv::Point(50, 150), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "Y偏移:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); - //textPainter.putText(Paint, "圆心X坐标:" + std::to_string(X_Circle), cv::Point(50, 150), cv::Scalar(0, 255, 0)); - //textPainter.putText(Paint, "圆心Y坐标:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); - - - } - else { - data->halconOK = false; - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 - //textPainter.putText(Paint, "算法异常", cv::Point(50, 300), cv::Scalar(0, 0, 255)); - std::string ngDetail = out_NgTails.ToString().Text(); - textPainter.putText(Paint, ngDetail, cv::Point(50, 300), cv::Scalar(0, 0, 255)); - } - - if (data->halconOK == true) { - textPainter.putText(Paint, "OK", cv::Point(50, 550), cv::Scalar(0, 255, 0)); - sqlMapData["结果"] = "OK"; - } - else { - textPainter.putText(Paint, "NG", cv::Point(50, 500), cv::Scalar(0, 0, 255)); - sqlMapData["结果"] = "NG"; - } - data->service->_dataRecorder->addData(QString::fromStdString(data->SNcode), sqlMapData); - - //int rate = MIN(Paint.cols, Paint.rows) / 20; - //TextRendererZH textPainter2(Paint.cols, Paint.rows, rate); - //textPainter2.putText(Paint, "获取图像时间:" + currentimenow.toString().toStdString(), cv::Point(50, Paint.rows-rate), cv::Scalar(0, 255, 255)); - //textPainter2.putText(Paint, "SN条码:" + data->SNcode, cv::Point(50, Paint.rows - (rate*2), cv::Scalar(0, 255, 255)); - data->insertResultImg(Paint); - - return true; - } - CATCH_EXCEPTIONS - qDebug() << "error3" << QString::fromStdString(data->SNcode); - exceptionResume(data, address, "异常"); - return false; -} -static bool YuHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address) { - try { - std::map sqlMapData; - sqlMapData["项目"] = "预焊"; - sqlMapData["相机"] = "相机" + address.triggerAddr; - ///////////////////取图//处理////////////////// - if (data->getOriImgSize() < 1) { - exceptionResume(data, address, "图片数量不满足要求"); - qDebug() << "图片数量不满足要求:" << data->getOriImgSize(); - return false; - } - - /*QDateTime currentimenow = QDateTime::currentDateTime();*/ - cv::Mat Paint; - cv::Mat src1 = data->getOriImg(0); - //src1 = cv::imread("D:\\04QCB82602000JFCA00600BN_Camera1_0_04-18-02.bmp",-1); - //src1 = cv::imread("C:\\Users\\Main\\MVS\\Data\\Image_20251205093757260.bmp", -1); - if (src1.channels() != 3) - cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); - else - src1.copyTo(Paint); - //处理算法 -#pragma region halcon处理 - std::string cameraNumberStr = "1", FixtureNumber = "1"; - if (address.triggerAddr == 19600) { - cameraNumberStr = "1"; - FixtureNumber = "1"; - } - else if (address.triggerAddr == 19602) { - cameraNumberStr = "1"; - FixtureNumber = "3"; - } - else if (address.triggerAddr == 19800) { - cameraNumberStr = "2"; - FixtureNumber = "2"; - } - else if (address.triggerAddr == 19802) { - cameraNumberStr = "2"; - FixtureNumber = "4"; - } - qDebug() << "Start1" << QString::fromStdString(data->SNcode); - data->customPath = "{imgSaveRootDir}/{yyyy-MM-dd}/{saveObjectName}{imageType}/{HH}H/{processResult}/相机" + QString::fromStdString(cameraNumberStr) + "孔" + QString::fromStdString(FixtureNumber);; - qDebug() << "Start2" << QString::fromStdString(data->SNcode); - - int pingDing_aoDing_Switch = data->service->_paramSyncManager->get("预焊","1平钉2凹钉", 2).toInt(); - int YuHanbiaoding_Switch = data->service->_paramSyncManager->get("预焊","模式选择:1标定0生产", 0).toInt(); - if(YuHanbiaoding_Switch == 1) - data->service->_paramSyncManager->edit("预焊", "1标定0生产", 0); - double tongXingJuThreshold = data->service->_paramSyncManager->get("预焊", "同心距上限", 0.1).toDouble(); - qDebug() << "Start3" << QString::fromStdString(data->SNcode); - HDevEngineCpp::HDevProcedure proc("_9_yuhan"); - qDebug() << "Start4" << QString::fromStdString(data->SNcode); - HalconCpp::HObject in_Image = MatToHObject(src1); - HDevProcedureCall procedureCall(proc); - qDebug() << "Start5" << QString::fromStdString(data->SNcode); - procedureCall.SetInputIconicParamObject("in_Image", in_Image); - procedureCall.SetInputCtrlParamTuple("in_ProductionType", YuHanbiaoding_Switch); - procedureCall.SetInputCtrlParamTuple("in_Resolution", HTuple(0.0069699)); - procedureCall.SetInputCtrlParamTuple("in_CameraNumber", HTuple(cameraNumberStr.c_str())); - procedureCall.SetInputCtrlParamTuple("in_FixtureNumber", HTuple(FixtureNumber.c_str())); - procedureCall.SetInputCtrlParamTuple("in_FilePath1", HTuple(("./halconFiles/template_yuhan/" + data->cameraKeyName + "/").c_str())); - procedureCall.SetInputCtrlParamTuple("in_FilePath2", HTuple("./halconFiles/base_yuhan/")); - procedureCall.SetInputCtrlParamTuple("in_NailType", pingDing_aoDing_Switch);//1:平钉 2:凹钉; - //procedureCall.SetInputCtrlParamTuple("in_LineType", "602"); - - procedureCall.Execute(); - - // *output(HTuple) : < / c> - // *out_Circle, 圆心坐标(y, x, radius) < / c > - // * out_Flag, 算法执行成功标志位, 0 : 算法执行失败 1 : 算法执行成功< / c> - // *out_OffsetX : 模组X轴的补偿量(mm) < / c > - // *out_OffsetY : 模组Y轴的补偿量(mm) < / c > - HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); - HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); - HTuple out_OffsetX = procedureCall.GetOutputCtrlParamTuple("out_OffsetX"); - HTuple out_OffsetY = procedureCall.GetOutputCtrlParamTuple("out_OffsetY"); - HTuple out_NgTails = procedureCall.GetOutputCtrlParamTuple("out_NgTails"); - HTuple out_CCDistance = procedureCall.GetOutputCtrlParamTuple("out_CCDistance"); - HTuple out_OffsetValue = procedureCall.GetOutputCtrlParamTuple("out_OffsetValue"); -#pragma endregion - int outFlag = out_Flag.D(); - //qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera::放钉" << "_SN::" << SN << ",放钉算法结果;" << out_Flag[0].D(); - TextRendererZH textPainter(Paint.cols, Paint.rows, 150); - textPainter.putText(Paint, "相机:" + cameraNumberStr + " 孔位" + FixtureNumber + (pingDing_aoDing_Switch == 1 ? " 平钉算法" : " 凹钉算法"), cv::Point(50, 0), cv::Scalar(0, 255, 0)); - if (outFlag == 1) { //-1为失败,无结果 - double offsetX = out_OffsetX.D(); - double offsetY = out_OffsetY.D(); - double offsetNum = out_OffsetValue.D(); - double CCDdist = out_CCDistance.D(); - //if (address.triggerAddr == 19600) - // offsetX -= 0.2; - //else if (address.triggerAddr == 19602) { - // offsetX -= 0.15; - // offsetY += 0.1; - //} - //else (address.triggerAddr == 19800) { - // offsetY -= 0.2; - //} - - //圆及十字 - cv::circle(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D()), out_Circle[2].D(), cv::Scalar(0, 255, 0), 5); - cv::line(Paint, cv::Point(out_Circle[1].D() - 300, out_Circle[0].D()), cv::Point(out_Circle[1].D() + 300, out_Circle[0].D()), cv::Scalar(0, 255, 0), 5); - cv::line(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D() - 300), cv::Point(out_Circle[1].D(), out_Circle[0].D() + 300), cv::Scalar(0, 255, 0), 5); - - textPainter.putText(Paint, "X偏移:" + std::to_string(offsetX), cv::Point(50, 150), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "Y偏移:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "同心距:" + std::to_string(CCDdist), cv::Point(50, 550), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "偏移值:" + std::to_string(offsetNum), cv::Point(50, 750), cv::Scalar(0, 255, 0)); - - if (std::abs(tongXingJuThreshold) < CCDdist) { - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_" + QByteArray::number(offsetNum));//发送偏移量 - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 - qDebug() << __FUNCTION__ << " line:" << __LINE__ << "同心距NG"; - textPainter.putText(Paint, "同心距NG", cv::Point(250, 950), cv::Scalar(0, 0, 255)); - data->halconOK = false; - } - else if (abs(offsetX) >= 6000 || abs(offsetY) >= 6000) { - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_" + QByteArray::number(offsetNum));//发送偏移量 - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 - qDebug() << __FUNCTION__ << " line:" << __LINE__ << " 预焊X偏移abs(resultData[0]) >= 6000,may err,set 0"; - data->halconOK = false; - } - else { - sqlMapData["偏移X"] = offsetX; - sqlMapData["偏移Y"] = offsetY; - if (offsetX || offsetY == 0) - { - offsetX += 0.01; - offsetY += 0.01; - } - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(offsetX));//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(offsetY));//发送偏移Y - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_" + QByteArray::number(offsetNum));//发送偏移量 - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - data->halconOK = true; - } - } - else { - data->halconOK = false; - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 - //textPainter.putText(Paint, "算法异常", cv::Point(50, 300), cv::Scalar(0, 0, 255)); - std::string ngDetail = out_NgTails.ToString().Text(); - textPainter.putText(Paint, ngDetail, cv::Point(50, 300), cv::Scalar(0, 0, 255)); - - } - - if (data->halconOK == true) { - textPainter.putText(Paint, "OK", cv::Point(50, 950), cv::Scalar(0, 255, 0)); - sqlMapData["结果"] = "OK"; - } - else { - textPainter.putText(Paint, "NG", cv::Point(50, 950), cv::Scalar(0, 0, 255)); - sqlMapData["结果"] = "NG"; - } - data->service->_dataRecorder->addData(QString::fromStdString(data->SNcode), sqlMapData); - - //int rate = MIN(Paint.cols, Paint.rows) / 20; - //TextRendererZH textPainter2(Paint.cols, Paint.rows, rate); - //textPainter2.putText(Paint, "获取图像时间:" + currentimenow.toString().toStdString(), cv::Point(50, Paint.rows-rate), cv::Scalar(0, 255, 255)); - //textPainter2.putText(Paint, "SN条码:" + data->SNcode, cv::Point(50, Paint.rows - (rate*2), cv::Scalar(0, 255, 255)); - - data->insertResultImg(Paint); - - return true; - } - CATCH_EXCEPTIONS - qDebug() << "error" << QString::fromStdString(data->SNcode); - exceptionResume(data, address, "异常"); - return false; -} - -bool aiEvaluate(std::shared_ptr data, cv::Mat& input,cv::Mat& Paint,int index, mfd_hh_models* models,cv::Size fillter) { - try { - HDevEngineCpp::HDevProcedure proc("_9_Crop_Inspection_Image_HJ"); - HalconCpp::HObject in_Image_2D = MatToHObject(input); - HDevProcedureCall procedureCall(proc); - procedureCall.SetInputIconicParamObject("in_Image", in_Image_2D); - procedureCall.SetInputCtrlParamTuple("in_CropType", 0); - procedureCall.SetInputCtrlParamTuple("in_FilePath", "./halconFiles/mfd_hh_cut/"); - procedureCall.Execute(); - HObject out_InspectionImage = procedureCall.GetOutputIconicParamObject("out_InspectionImage"); - HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); - HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); - cv::Mat forSeg = HObject2Mat(out_InspectionImage); - //forSeg(cv::Rect(center.x - 650, center.y - 650, 1300, 1300)).copyTo(forSeg); - cv::resize(forSeg, forSeg, cv::Size(), 0.5, 0.5); - data->insertSegOriginImg(forSeg); - cv::Mat segResult = ~models->seg_hanHou->Evaluate(forSeg); - std::vector>contours;//点集 - cv::findContours(segResult, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); // 寻找白色区域并储存为点集 - std::vector> clsImgs; - bool isNg = false; - TextRendererZH textPainter(Paint.cols, Paint.rows, 20); - for (int i = 0; i < contours.size(); i++) {//遍历点集 - cv::Rect rect = cv::boundingRect(contours[i]);//最小外接矩形 - - if (rect.height > fillter.height || rect.width > fillter.width) {// 过滤 只有都大于10像素才进检测 - cv::Mat clsImg = cutImg2Cls(rect, forSeg); - int clsResult = models->cls_hanHou->Evaluate(clsImg); - double prob = models->cls_hanHou->getProbability(); - clsImgs.push_back({ "Result" + std::to_string(clsResult) + "_prob" + std::to_string(prob),clsImg }); - if (clsResult != 0 && prob > 0.6) { - cv::drawContours(Paint, contours, i, cv::Scalar(0, 0, 255), 3, 8, cv::noArray(), INT_MAX); - std::string text; - switch (clsResult) - { - case 1: - text = "焊渣"; - break; - case 2: - text = "裂纹"; - break; - case 3: - text = "炸焊"; - break; - case 4: - text = "焊烟"; - break; - } - textPainter.putText(Paint, text, cv::Point(0,0), cv::Scalar(0, 0, 255)); - isNg = true; - } - } - } - data->insertClsOriginImg(clsImgs); - return isNg; - } - CATCH_EXCEPTIONS - return false; - -} -static bool HanHou2D(std::shared_ptr data, mfdHanHouParam* _detParams, yh_581_address address) { - cv::Mat segResult; - cv::Mat heightImg; - data->SNcode += "_" + std::to_string(address.index); - try { - if (data->getOriImgSize() < 1) { - qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); - return false; - } - cv::Mat Paint; - cv::Mat src1 = data->getOriImg(0); - //src1 = cv::imread("D:\\test\\04QCB82602000JFCD00776BN_Camera7_0_04-56-43.bmp", -1); - //处理算法 -#pragma region halcon处理 - cv::Mat forHalcon; - cv::resize(src1, forHalcon, cv::Size(), 0.5, 0.5); - if (forHalcon.channels() != 3) - cv::cvtColor(forHalcon, Paint, cv::COLOR_GRAY2BGR); - else - forHalcon.copyTo(Paint); - segResult = _detParams->models[address.index].seg_hanHouDingWei->Evaluate(forHalcon); - data->insertSegOriginImg(segResult); - _detParams->waitImage(heightImg); - HDevEngineCpp::HDevProcedure proc("_9_Cal_2D_3D_PostWelding"); - HDevProcedureCall procedureCall(proc); - - HalconCpp::HObject in_Image_2D = MatToHObject(forHalcon); - HalconCpp::HObject in_Image3DH = MatToHObject(heightImg); - HalconCpp::HObject in_Image_seg = MatToHObject(segResult); - - procedureCall.SetInputIconicParamObject("in_Image_2D", in_Image_2D); - procedureCall.SetInputIconicParamObject("in_Image3DH", in_Image3DH); - procedureCall.SetInputIconicParamObject("in_Image_seg", in_Image_seg); - - procedureCall.SetInputCtrlParamTuple("in_xResolution", HTuple(0.0069699)); - procedureCall.SetInputCtrlParamTuple("in_yResolution", HTuple(0.0069699)); - procedureCall.SetInputCtrlParamTuple("in_zResolution", HTuple(0.0069699)); - procedureCall.SetInputCtrlParamTuple("in_FilePath", "./halconFiles/mfd_hh/"); - procedureCall.SetInputCtrlParamTuple("in_2D_pix", HTuple(0.0069699)); - procedureCall.SetInputCtrlParamTuple("in_Fixture_Number", 1); - HTuple OffsetValue; - for (int i = 0; i < 8; i++) - OffsetValue.Append(0); - procedureCall.SetInputCtrlParamTuple("in_offsetValue", OffsetValue); - procedureCall.Execute(); - qDebug() << "test flagxxx1"; - HTuple out_CC_Distance = procedureCall.GetOutputCtrlParamTuple("out_CC_Distance"); - qDebug() << "test flagxxx2"; - HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); - qDebug() << "test flagxxx3"; - HTuple out_DefectHeight = procedureCall.GetOutputCtrlParamTuple("out_MFDHeight"); - qDebug() << "test flagxxx4"; - HTuple Out_Angle = procedureCall.GetOutputCtrlParamTuple("out_Angle"); - qDebug() << "test flagxxx5"; - HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); - qDebug() << "test flagxxx6"; - HTuple out_NgDetails = procedureCall.GetOutputCtrlParamTuple("out_NgDetails"); -#pragma endregion - qDebug() << "test flag1"; - int outFlag = out_Flag.D(); - QByteArray plcResult = "1"; - TextRendererZH textPainter(Paint.cols, Paint.rows, 100); - if (outFlag == 1) { //-1为失败,无结果 - cv::Mat forHanddleAi; - qDebug() << "test flag2"; - double CC_Distance = out_CC_Distance.D(); - qDebug() << "test flag3"; - for (int i = 0; i < out_Circle.Length() / 3; i++) { - cv::Point2f center1(out_Circle[i * 3 + 1].D(), out_Circle[i * 3].D()); - double radius1 = out_Circle[i * 3 + 2].D(); - cv::circle(Paint, center1 / 2, radius1 / 2, cv::Scalar(0, 255, 0), 3); - } - - qDebug() << "test flag5"; - double defectHeight = out_DefectHeight.D();//密封钉高度 - qDebug() << "test flag6"; - double angle = Out_Angle.D();//密封钉角度 - - textPainter.putText(Paint, "密封钉高度:" + std::to_string(defectHeight), cv::Point(50, 0), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "密封钉角度:" + std::to_string(angle), cv::Point(50, 350), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "密封钉同心距:" + std::to_string(CC_Distance), cv::Point(50, 550), cv::Scalar(0, 255, 0)); - - bool result = aiEvaluate(data, src1,Paint, address.index, &_detParams->models[address.index], - cv::Size(data->service->_paramSyncManager->get("焊后", "过滤宽(pix)", 50).toDouble(), data->service->_paramSyncManager->get("焊后", "过滤高(pix)", 50).toDouble())); - qDebug() << "test flag7"; - if (result == false && data->service->_paramSyncManager->get("焊后", "模型判定启用", false).toBool()) - plcResult = "2"; - - //cv::circle(Paint, center2 / 2, radius2 / 2, cv::Scalar(0, 255, 0), 3); - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(CC_Distance));//发送同心距 - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(defectHeight));//发送密封钉高度 - } - else { - data->halconOK = false; - textPainter.putText(Paint, "NG", cv::Point(50, 300), cv::Scalar(0, 0, 255)); - textPainter.putText(Paint, std::to_string(out_NgDetails.D()), cv::Point(50, 450), cv::Scalar(0, 0, 255)); - - } - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - data->insertResultImg(Paint); - - return true; - } - CATCH_EXCEPTIONS - exceptionResume(data, address, "异常"); -} -static bool HanHou3D(std::shared_ptr data, mfdHanHouParam* _detParams, yh_581_address address) { - try { - data->SNcode += "_" + std::to_string(address.index); - ///////////////////取图//处理////////////////// - //if (data->getOriImgSize() < 1) { - // qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); - // return false; - //} - cv::Mat Paint; - cv::Mat src1 = data->getOriImg(0); - cv::Mat heightImg = data->getOriImg(1); - //heightImg = cv::imread("D:\\test\\04QCB82602000JFCD00776BN_Camera8_1_04-56-43.tiff", -1); - if (heightImg.channels() != 3) - cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); - else - src1.copyTo(Paint); - _detParams->insertImg(heightImg); - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - data->insertResultImg(Paint); - - return true; - } - CATCH_EXCEPTIONS - exceptionResume(data, address, "异常"); -} -#pragma endregion -class _581_Mfd : public mission { -public: - _581_Single_triggerTemplate< mfd_detParams> yh_1; - _581_Single_triggerTemplate< mfd_detParams> yh_2; - - _581_Single_triggerTemplate< mfd_detParams> mh_1; - _581_Single_triggerTemplate< mfd_detParams> mh_2; - _581_Single_triggerTemplate< mfd_detParams> mh_3; - _581_Single_triggerTemplate< mfd_detParams> mh_4; - - _581_Union_triggerTemplate< mfdHanHouParam> hh_2D; - _581_Union_triggerTemplate< mfdHanHouParam> hh_3D; - - mfd_detParams _params; - - mfdHanHouParam _hhParams; - void onConfigReloaded() { - HDevEngineCpp::HDevEngine myEngine; - myEngine.SetProcedurePath("./halconAlgo/AlgoLibrary_602.hdpl"); - for (auto& model : _hhParams.models) { - model.seg_hanHou = handdleAI::CreateSegObjS("./handleaiModels/seg_hanhou.BHD", 16, 0); - model.cls_hanHou = handdleAI::CreateClsObjV3("./handleaiModels/cls_hanhou.BHD", 0, 1); - model.seg_hanHouDingWei = handdleAI::CreateSegObjS("./handleaiModels/seg_hanhouDingWei.BHD", 16, 0); - } - } - bool initCustom() { -#pragma region 注册触发器回调 - yh_1.init("Camera1", "预焊1", 19600, 2, 1, &_params, _serviceManager, bridge, YuHan, "预焊CCD"); - yh_2.init("Camera2", "预焊2", 19800, 2, 1, &_params, _serviceManager, bridge, YuHan, "预焊CCD"); - - mh_1.init("Camera3", "满焊1", 20000, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); - mh_2.init("Camera4", "满焊2", 20200, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); - mh_3.init("Camera5", "满焊3", 20400, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); - mh_4.init("Camera6", "满焊4", 20600, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); - - hh_2D.init("Camera7", "焊后2d", 20800, 4, 1, &_hhParams, _serviceManager, bridge, HanHou2D, "焊后2D"); - hh_3D.init("Camera8", "焊后3d", 21000, 4, 1, &_hhParams, _serviceManager, bridge, HanHou3D, "焊后3D"); -#pragma endregion - return true; - } - - - //9600 - -}; - - - - - -#pragma once \ No newline at end of file diff --git a/src/mission/581_YuHan_hanqian/581_YuHan_hanqian.h b/src/mission/581_YuHan_hanqian/581_YuHan_hanqian.h deleted file mode 100644 index 2059c63..0000000 --- a/src/mission/581_YuHan_hanqian/581_YuHan_hanqian.h +++ /dev/null @@ -1,294 +0,0 @@ -#include -#include -#pragma region 算法 -#include -#include -#include -using namespace HDevEngineCpp; -#pragma endregion -class yuHanHanQianParams { -public: - handdleAI::pSegmentation* segDingWei = nullptr; - - handdleAI::pSegmentation* segJiErFanZhe = nullptr; - handdleAI::pClassification* clsJiErFanZhe = nullptr; - handdleAI::pClassification* clsOverView = nullptr; - bool isTong = true; -}; -void putSnAndDetTime(cv::Mat& Paint,const std::string& sn,int textSize = 30, const std::string& extraText = "") { - TextRendererZH textPainter(Paint.cols, Paint.rows, textSize); - textPainter.putText(Paint, "SN:" + sn + " time:" + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); -} -cv::Rect getImageLocateRect(cv::Mat& src, handdleAI::pSegmentation* seg, double bounus = 0.1) { - cv::Mat tmpImg; - cv::resize(src, tmpImg, cv::Size(), bounus, bounus); - cv::Mat ouput = ~seg->Evaluate(tmpImg); - cv::erode(ouput, ouput, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); - cv::dilate(ouput, ouput, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); - auto rect = findMaxAreaContour(ouput); - rect.x /= bounus; rect.y /= bounus; - rect.width /= bounus; rect.height /= bounus; - return rect; -} -bool handleAiProcess(cv::Mat& seg_detImg, cv::Mat& seg_Paint, - handdleAI::pSegmentation* segPtr, handdleAI::pClassification* clsPtr, - std::vector< std::pair >& clsImgs,double probThreshold,cv::Size fillter = cv::Size(300,300)){ - cv::Mat segResizeImg; - float resizeRate = 1.0; - cv::resize(seg_detImg, segResizeImg, cv::Size(), 1.0 / resizeRate, 1.0 / resizeRate); - cv::Mat seg_ResultImg = ~segPtr->Evaluate(segResizeImg); - cv::resize(seg_ResultImg, seg_ResultImg, cv::Size(), resizeRate, resizeRate); - cv::erode(seg_ResultImg, seg_ResultImg, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); - cv::dilate(seg_ResultImg, seg_ResultImg, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); - std::vector> contours; - cv::findContours(seg_ResultImg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); - bool finalNg = false; - TextRendererZH textPainter(seg_Paint.cols, seg_Paint.rows, 20); - clsImgs.push_back({ "seg" ,seg_ResultImg }); - for (int i = 0; i < contours.size(); i++) - { - cv::Rect cls_rect = cv::boundingRect(contours[i]); - if (cls_rect.width > fillter.width && cls_rect.height > fillter.height) { - std::vector clsImgVec = cutImg2ClsVec(cls_rect, seg_detImg); - std::vector clsRes;//true ok - bool isNg = false; - double maxNgProb = 0.0; - for (auto& cls : clsImgVec) { - cv::resize(cls, cls, cv::Size(128, 128));//根据需要设定大小 - int Result = clsPtr->Evaluate(cls); - double prob = clsPtr->getProbability(); - if (Result != 0 && prob > probThreshold) { - isNg = true; - maxNgProb = std::max(maxNgProb, prob); - } - clsRes.push_back(Result); - clsImgs.push_back({ "Result" + std::to_string(Result) + "_prob" + std::to_string(prob) ,cls }); - } - { - cv::Mat testCls; - cv::resize(seg_detImg(cls_rect), testCls, cv::Size(128, 128)); - int Result = clsPtr->Evaluate(testCls); - double prob = clsPtr->getProbability(); - if (Result != 0 && prob > probThreshold) { - isNg = true; - maxNgProb = std::max(maxNgProb, prob); - } - clsImgs.push_back({ "testClsResult" + std::to_string(Result) + "_prob" + std::to_string(prob),testCls }); - } - if (isNg == true) { - cv::Scalar color = cv::Scalar(0, 255, 255, 30); - textPainter.putText(seg_Paint, "w" + std::to_string(cls_rect.width) - + " h" + std::to_string(cls_rect.height) - + " a" + std::to_string(cv::contourArea(contours[i])) - + " p" + std::to_string(maxNgProb) - , cv::Point(cls_rect.x, cls_rect.y), cv::Scalar(255, 255, 0)); - cv::rectangle(seg_Paint, cls_rect, color, 4); - drawContours(seg_Paint, contours, i, cv::Scalar(0, 0, 255, 30), 3); - finalNg = true; - } - else { - drawContours(seg_Paint, contours, i, cv::Scalar(0, 255, 0, 30), 3); - } - } - else if (cls_rect.width > 100 && cls_rect.height > 100) { - std::vector clsImgVec = cutImg2ClsVec(cls_rect, seg_detImg); - std::vector clsRes;//true ok - double maxNgProb = 0.0; - for (auto& cls : clsImgVec) { - cv::resize(cls, cls, cv::Size(128, 128));//根据需要设定大小 - int Result = clsPtr->Evaluate(cls); - double prob = clsPtr->getProbability(); - if (Result != 0 && prob > probThreshold) { - maxNgProb = std::max(maxNgProb, prob); - } - clsRes.push_back(Result); - clsImgs.push_back({ "Result" + std::to_string(Result) + "_prob" + std::to_string(prob) ,cls }); - } - { - cv::Mat testCls; - cv::resize(seg_detImg(cls_rect), testCls, cv::Size(128, 128)); - int Result = clsPtr->Evaluate(testCls); - double prob = clsPtr->getProbability(); - if (Result != 0 && prob > probThreshold) { - maxNgProb = std::max(maxNgProb, prob); - } - clsImgs.push_back({ "testClsResult" + std::to_string(Result) + "_prob" + std::to_string(prob),testCls }); - } - } - - } - return finalNg; -} -bool jiErFanZhe(std::shared_ptr data, yuHanHanQianParams* _detParams, sx_581_address address) { - try { - - //存图数据加保存队列,加锁,避免数据访问冲突 - QByteArray plcResult = "1"; - cv::Mat sourceImg; - data->getOriImg(0).copyTo(sourceImg); - //sourceImg = cv::imread("D:\\60A01MB5601FB2B213016_Camera2_ORI_0_04-13-55-889.bmp", -1); - cv::Mat Paint; - cv::resize(sourceImg, sourceImg, cv::Size(), 0.5, 0.5); - sourceImg.copyTo(Paint); - if (Paint.channels() == 1) - cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - //halconProcess(sourceImg, Paint); - float resizeRate = 2.5; - auto rect = getImageLocateRect(sourceImg, _detParams->segDingWei, 1.0 / resizeRate);//定位 - cv::Mat seg_CutImg; - rect = rect & (cv::Rect(0, 0, sourceImg.cols, sourceImg.rows));//防止越界 - bool isNG = false; - std::vector< std::pair > clsImgs; - if (rect.height > 800 ) {//定位极耳过长过短 - cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 4); - isNG = true; - } - else if (rect.width > 400 || rect.height > 400) {//定位成功 - sourceImg(rect).copyTo(seg_CutImg); - data->insertSegOriginImg(seg_CutImg); - - if (data->cameraKeyName == "Camera2" || data->cameraKeyName == "Camera4" || data->cameraKeyName == "Camera6" || data->cameraKeyName == "Camera8") {//底部 - rect.y += 300; - rect.height -= 300; - } - else {//顶部 - rect.y += 150; - rect.height -= 150; - } - - cv::Size fillter; - if (data->cameraKeyName == "Camera3" || data->cameraKeyName == "Camera4" || data->cameraKeyName == "Camera7" || data->cameraKeyName == "Camera8") { - fillter = cv::Size(data->service->_paramSyncManager->get("焊前", "铜过滤1_width", 200).toInt(), - data->service->_paramSyncManager->get("焊前", "铜过滤1_height", 200).toInt()); - } - else if (data->cameraKeyName == "Camera1" || data->cameraKeyName == "Camera2" || data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera6") { - fillter = cv::Size(data->service->_paramSyncManager->get("焊前", "铝过滤1_width", 200).toInt(), - data->service->_paramSyncManager->get("焊前", "铝过滤1_height", 200).toInt()); - } - isNG = handleAiProcess(sourceImg(rect), Paint(rect), _detParams->segJiErFanZhe, _detParams->clsJiErFanZhe, clsImgs, data->service->_paramSyncManager->get("焊前", "prob", 0.99998).toDouble(), fillter); - - //if (data->cameraKeyName == "Camera3" || data->cameraKeyName == "Camera4" || data->cameraKeyName == "Camera7" || data->cameraKeyName == "Camera8") { - // TextRendererZH textPainter(Paint.cols, Paint.rows, 100); - // int testClsResult = _detParams->clsOverView->Evaluate(testImg1); - // double testClsProb = _detParams->clsOverView->getProbability(); - // if(testClsResult == 0) - // textPainter.putText(Paint, "(仅测试不排废)大图CLS测试结果" + std::to_string(testClsResult) + ",prob" + std::to_string(testClsProb), cv::Point(0, 0), cv::Scalar(0, 255, 0)); - // else - // textPainter.putText(Paint, "(仅测试不排废)大图CLS测试结果" + std::to_string(testClsResult) + ",prob" + std::to_string(testClsProb), cv::Point(0, 0), cv::Scalar(0, 0, 255)); - - // if (testClsResult == 1 && testClsProb > 0.995) { - // data->aiDetOK = false; - // } - //} - - cv::rectangle(Paint, rect, cv::Scalar(0, 255, 255), 4); - data->insertClsOriginImg(clsImgs); - } - else { - isNG = true; - } - TextRendererZH textPainter(Paint.cols, Paint.rows, 300); - if (isNG == false)//OK - { - textPainter.putText(Paint, "OK", cv::Point(100, 50), cv::Scalar(0, 255, 0)); - } - else - { - textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); - data->aiDetOK = false; - if (data->cameraKeyName == "Camera1" || data->cameraKeyName == "Camera3" || data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera7") { - if (data->service->_paramSyncManager->get("极耳翻折", "极耳翻折顶部判定启用", false).toBool() == true) - plcResult = "2"; - } - else { - if (data->service->_paramSyncManager->get("极耳翻折", "极耳翻折底部判定启用", false).toBool() == true) - plcResult = "2"; - } - } - putSnAndDetTime(Paint, data->SNcode,60); - std::vector defects; - if (plcResult == "2")defects.push_back("焊前NG"); - data->service->_missionResultHandle->toBarChart(data->SNcode, defects); - if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool() == true) - plcResult = "1";//强制OK - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 - - - cv::Mat seg_LocateImg; - cv::resize(sourceImg, seg_LocateImg, cv::Size(), 0.4, 0.4); - data->insertSegOriginImg(seg_LocateImg); - data->insertResultImg(Paint); - return true; - - } - CATCH_EXCEPTIONS - - qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode); - cv::Mat Paint; - data->getOriImg(0).copyTo(Paint); - if (Paint.channels() == 1) - cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - TextRendererZH textPainter(Paint.cols, Paint.rows, 15); - textPainter.putText(Paint, "NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); - cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); - data->insertResultImg(Paint); - data->halconOK = false; - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 - return true; -} -class _581_YuHan_hanqian : public mission { -public: - _581_SX_Single_triggerTemplate< yuHanHanQianParams> in_lv_top_1; - _581_SX_Single_triggerTemplate< yuHanHanQianParams> in_lv_down_2; - _581_SX_Single_triggerTemplate< yuHanHanQianParams> in_tong_top_3; - _581_SX_Single_triggerTemplate< yuHanHanQianParams> in_tong_down_4; - - _581_SX_Single_triggerTemplate< yuHanHanQianParams> out_lv_top_5; - _581_SX_Single_triggerTemplate< yuHanHanQianParams> out_lv_down_6; - _581_SX_Single_triggerTemplate< yuHanHanQianParams> out_tong_top_7; - _581_SX_Single_triggerTemplate< yuHanHanQianParams> out_tong_down_8; - std::vector < yuHanHanQianParams> tongDetParams = std::vector < yuHanHanQianParams>(4); - std::vector < yuHanHanQianParams> lvDetParams = std::vector < yuHanHanQianParams>(4); - bool initOnce = false; - void onConfigReloaded() { - HDevEngineCpp::HDevEngine myEngine; - myEngine.SetProcedurePath("./halconAlgoFiles/AlgoLibrary_628_1.hdpl"); - - if (initOnce == false) { - for (auto& params : tongDetParams) { - params.segDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanqian/hanqianTong_dingwei_seg.BHD", 16, 0); - params.segJiErFanZhe = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanqian/hanqian_T-seg.BHD", 16, 0); - params.clsJiErFanZhe = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanqian/hanqian_T-cls.BHD", 0, 1); - params.clsOverView = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanqian/hanqian_overViewCls_T.BHD", 0, 1); - } - for (auto& params : lvDetParams) { - params.segDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanqian/hanqianLv_dingwei_seg.BHD", 16, 1); - params.segJiErFanZhe = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanqian/hanqian_L-seg.BHD", 16, 1); - params.clsJiErFanZhe = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanqian/hanqian_L-cls.BHD", 1, 1); - params.clsOverView = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanqian/hanqian_overViewCls_T.BHD", 1, 1); - params.isTong = false; - } - initOnce = true; - } - } - bool initCustom() { -#pragma region 注册触发器回调 - in_lv_top_1.init("Camera1", "极耳翻折-铝-内-相机1(顶部)", { { 682, 1,{760},"683","670",-1} }, 1, &lvDetParams[0], _serviceManager, bridge, jiErFanZhe); - in_lv_down_2.init("Camera2", "极耳翻折-铝-内-相机2(底部)", { { 684, 1,{760},"685","672",-1} }, 1, &lvDetParams[1], _serviceManager, bridge, jiErFanZhe); - in_tong_top_3.init("Camera3", "极耳翻折-铜-内-相机3(顶部)", { { 686,1,{760},"687","674",-1} }, 1, &tongDetParams[0], _serviceManager, bridge, jiErFanZhe); - in_tong_down_4.init("Camera4", "极耳翻折-铜-内-相机4(底部)", { { 688, 1,{760},"689","676",-1} }, 1, &tongDetParams[1], _serviceManager, bridge, jiErFanZhe); - - out_lv_top_5.init("Camera5", "极耳翻折-铝-外-相机5(顶部)", { { 702,1,{720},"703","710",-1} }, 1, &lvDetParams[2], _serviceManager, bridge, jiErFanZhe); - out_lv_down_6.init("Camera6", "极耳翻折-铝-外-相机6(底部)", { { 704, 1,{720},"705","712",-1} }, 1, &lvDetParams[3], _serviceManager, bridge, jiErFanZhe); - out_tong_top_7.init("Camera7", "极耳翻折-铜-外-相机7(顶部)", { { 706, 1,{720},"707","714",-1} }, 1, &tongDetParams[2], _serviceManager, bridge, jiErFanZhe); - out_tong_down_8.init("Camera8", "极耳翻折-铜-外-相机8(底部)", { { 708, 1,{720},"709","716",-1} }, 1, &tongDetParams[3], _serviceManager, bridge, jiErFanZhe); -#pragma endregion - return true; - } -}; - - - - - -#pragma once \ No newline at end of file diff --git a/src/mission/601/601_RLJ_DingGai/601_RLJ_DingGai.cpp b/src/mission/601/601_RLJ_DingGai/601_RLJ_DingGai.cpp index 27aa465..c6d52f9 100644 --- a/src/mission/601/601_RLJ_DingGai/601_RLJ_DingGai.cpp +++ b/src/mission/601/601_RLJ_DingGai/601_RLJ_DingGai.cpp @@ -4,7 +4,6 @@ struct PanitJob { cv::Point p; }; - bool ALGO_maJiaReRong(std::shared_ptr data, maJiaParam* _detParams, sx_581_address address) { try { qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; @@ -16,20 +15,23 @@ bool ALGO_maJiaReRong(std::shared_ptr data, maJiaParam* _detParams, sx return false; } auto sourceImg = data->getOriImg(0); - switch (address.triggerValue) { - case 1: - sourceImg = cv::imread("D:\\testImage\\601\\majia (1).bmp", -1); - break; - case 2: - sourceImg = cv::imread("D:\\testImage\\601\\majia (2).bmp", -1); - break; - } + //switch (address.triggerValue) { + //case 1: + // sourceImg = cv::imread("D:\\testImage\\601\\majia (1).bmp", -1); + // break; + //case 2: + // sourceImg = cv::imread("D:\\testImage\\601\\majia (2).bmp", -1); + // break; + //} sourceImg(cv::Rect(1700, 700, 2200, 2200)).copyTo(sourceImg); if (sourceImg.rows == 100) { cv::Mat Paint; sourceImg.copyTo(Paint); - cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); - return false; + cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送异常结果 + qCritical() << QString::fromStdString(data->cameraKeyName) << QString::fromStdString(data->SNcode) << "相机取图超时"; + data->insertResultImg(Paint); + return false; } cv::Mat Paint; sourceImg.copyTo(Paint); @@ -111,6 +113,7 @@ bool ALGO_maJiaReRong(std::shared_ptr data, maJiaParam* _detParams, sx _detParams->lastResult = "1";//复位结果 data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//发送结果 } + if (plcResult == "1") data->service->_missionResultHandle->toBarChart(data->SNcode, { }); else @@ -119,15 +122,18 @@ bool ALGO_maJiaReRong(std::shared_ptr data, maJiaParam* _detParams, sx cv::resize(Paint, Paint, cv::Size(), 0.25, 0.25); data->insertResultImg(Paint); + if (isNG == false && data->service->_paramSyncManager->get("马甲热熔", "马甲热熔只存NG原图启用", true).toBool())//结果为OK且第二个条件为TRUE时不存图 + data->deleteOriImg(0); return true; } CATCH_EXCEPTIONS data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送异常结果 return false; } -static bool ALGO_hanQianDingWei(std::shared_ptr data, detParams* _detParams, sx_581_address address) { +bool ALGO_hanQianDingWei(std::shared_ptr data, detParams* _detParams, sx_581_address address) { try { qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + ///////////////////取图//处理////////////////// if (data->getOriImgSize() < 1) { @@ -135,46 +141,43 @@ static bool ALGO_hanQianDingWei(std::shared_ptr data, detParams* _detP return false; } auto sourceImg = data->getOriImg(0); - sourceImg = cv::imread("D:\\testImage\\601\\hanqian.bmp", -1); - QThread::msleep(200); - + //sourceImg = cv::imread("D:\\601\\hqdw.bmp", -1); if (sourceImg.rows == 100) { cv::Mat Paint; sourceImg.copyTo(Paint); cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送异常结果 + qCritical() << QString::fromStdString(data->cameraKeyName) << QString::fromStdString(data->SNcode) << "相机取图超时"; + data->insertResultImg(Paint); return false; } cv::Mat Paint; sourceImg.copyTo(Paint); - HTuple out_ColorType, out_Distance, make; + HDevEngineCpp::HDevProcedure proc("HQDW_Color_Inspection"); + HDevEngineCpp::HDevProcedureCall procedureCall(proc); + + HObject inputImage = MatToHObject(sourceImg); + HTuple in_Resolution(0.040766), in_FilePath("./halconFiles/"); + + + double HeightLeft_jsonvlaue = data->service->_paramSyncManager->get("焊前定位", "焊前左侧Y向高度差", 2.0).toDouble(); + double HeightRight_jsonvlaue = data->service->_paramSyncManager->get("焊前定位", "焊前右侧Y向高度差", 2.0).toDouble(); + double SlantAngle_jsonvlaue = data->service->_paramSyncManager->get("焊前定位", "焊前盖板上下边缘膜的角度", 0.6).toDouble(); + + procedureCall.SetInputIconicParamObject("in_Image", inputImage); + procedureCall.SetInputCtrlParamTuple("in_Resolution", in_Resolution); + procedureCall.SetInputCtrlParamTuple("in_FilePath", in_FilePath); + procedureCall.Execute();//执行halcon程序 + HTuple out_ColorType, out_Distance, make, out_SlantAngle, out_Height;; HObject out_DisplayImage; - { - HDevEngineCpp::HDevProcedure proc("HQDW_Color_Inspection"); - HDevEngineCpp::HDevProcedureCall procedureCall(proc); - - HObject inputImage = MatToHObject(sourceImg); - HTuple in_Resolution(0.040766), in_FilePath("./halconFiles/"); - - - double HeightLeft_jsonvlaue = data->service->_paramSyncManager->get("焊前定位", "焊前左侧Y向高度差", 2.0).toDouble(); - double HeightRight_jsonvlaue = data->service->_paramSyncManager->get("焊前定位", "焊前右侧Y向高度差", 2.0).toDouble(); - double SlantAngle_jsonvlaue = data->service->_paramSyncManager->get("焊前定位", "焊前盖板上下边缘膜的角度", 0.6).toDouble(); - - procedureCall.SetInputIconicParamObject("in_Image", inputImage); - procedureCall.SetInputCtrlParamTuple("in_Resolution", in_Resolution); - procedureCall.SetInputCtrlParamTuple("in_FilePath", in_FilePath); - procedureCall.Execute();//执行halcon程序 - - - //out_LinePoint = procedureCall.GetOutputCtrlParamTuple("out_LinePoint");//获取输出控制类变量 - out_ColorType = procedureCall.GetOutputCtrlParamTuple("out_ColorType");//获取输出控制类变量 - //out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle");//获取输出控制类变量 - out_Distance = procedureCall.GetOutputCtrlParamTuple("out_Distance");//获取输出控制类变量 - //out_Height = procedureCall.GetOutputCtrlParamTuple("out_Height");//获取输出控制类变量 - //out_SlantAngle = procedureCall.GetOutputCtrlParamTuple("out_SlantAngle");//获取输出控制类变量 - make = procedureCall.GetOutputCtrlParamTuple("out_Flag");//获取输出控制类变量 - out_DisplayImage = procedureCall.GetOutputIconicParamObject("out_DisplayImage");//获取输出控制类变量 - } + //out_LinePoint = procedureCall.GetOutputCtrlParamTuple("out_LinePoint");//获取输出控制类变量 + out_ColorType = procedureCall.GetOutputCtrlParamTuple("out_ColorType");//获取输出控制类变量 + //out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle");//获取输出控制类变量 + out_Distance = procedureCall.GetOutputCtrlParamTuple("out_Distance");//获取输出控制类变量 + out_Height = procedureCall.GetOutputCtrlParamTuple("out_Height");//获取输出控制类变量 + out_SlantAngle = procedureCall.GetOutputCtrlParamTuple("out_SlantAngle");//获取输出控制类变量 + make = procedureCall.GetOutputCtrlParamTuple("out_Flag");//获取输出控制类变量 + out_DisplayImage = procedureCall.GetOutputIconicParamObject("out_DisplayImage");//获取输出控制类变量 int flag = make.D(); QString tmpFlag = "1"; // Output(HTuple): out_ColorType, 1, 铜; 2,铝 @@ -182,75 +185,79 @@ static bool ALGO_hanQianDingWei(std::shared_ptr data, detParams* _detP //正确的应该左边铝 右边铜 QByteArray plcResult = "1"; - TextRendererZH textPainter(Paint.cols, Paint.rows, 300); - if (flag == 1) - { - + cvPainter textPainter; + if (flag == 1) { int HeightLeftFlag = 1; int HeightRightFlag = 1; int AlgelFlag = 1; - Paint = HObject2Mat(out_DisplayImage); textPainter.appendPaintJob("膜上边到注液孔:" + std::to_string(out_Distance[0].D()), cv::Point(800, 50), cv::Scalar(0, 255, 0)); - textPainter.appendPaintJob("膜下边到注液孔:" + std::to_string(out_Distance[1].D()), cv::Point(800, 350), cv::Scalar(0, 255, 0)); - textPainter.executePaintJobs(Paint); - /* if (out_Height[0].D() > HeightLeft_jsonvlaue) - { + textPainter.appendPaintJob("膜下边到注液孔:" + std::to_string(out_Distance[1].D()), cv::Point(800, 250), cv::Scalar(0, 255, 0)); + if (out_Height[0].D() > HeightLeft_jsonvlaue) { HeightLeftFlag = 2; - textPainter.putText(Paint, "左侧Y向高度差:" + std::to_string(out_Height[0].D()), cv::Point(800, 650), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob("左侧Y向高度差:" + std::to_string(out_Height[0].D()), cv::Point(800, 450), cv::Scalar(0, 0, 255)); } else - textPainter.putText(Paint, "左侧Y向高度差:" + std::to_string(out_Height[0].D()), cv::Point(800, 650), cv::Scalar(0, 255, 0));*/ + textPainter.appendPaintJob("左侧Y向高度差:" + std::to_string(out_Height[0].D()), cv::Point(800, 450), cv::Scalar(0, 255, 0)); - //if (out_Height[1].D() > HeightRight_jsonvlaue) - //{ - // HeightRightFlag = 2; - // textPainter.putText(Paint, "右侧Y向高度差:" + std::to_string(out_Height[1].D()), cv::Point(800, 950), cv::Scalar(0, 0, 255)); - //} - //else - // textPainter.putText(Paint, "右侧Y向高度差:" + std::to_string(out_Height[1].D()), cv::Point(800, 950), cv::Scalar(0, 255, 0)); - //if (out_SlantAngle.D() > SlantAngle_jsonvlaue) - //{ - // AlgelFlag = 2; - // textPainter.putText(Paint, "盖板上下边缘膜的角度:" + std::to_string(out_SlantAngle.D()), cv::Point(800, 1250), cv::Scalar(0, 0, 255)); - //} - //else - // textPainter.putText(Paint, "盖板上下边缘膜的角度:" + std::to_string(out_SlantAngle.D()), cv::Point(800, 1250), cv::Scalar(0, 255, 0)); + if (out_Height[1].D() > HeightRight_jsonvlaue) { + HeightRightFlag = 2; + textPainter.appendPaintJob("右侧Y向高度差:" + std::to_string(out_Height[1].D()), cv::Point(800, 650), cv::Scalar(0, 0, 255)); + } + else + textPainter.appendPaintJob("右侧Y向高度差:" + std::to_string(out_Height[1].D()), cv::Point(800, 650), cv::Scalar(0, 255, 0)); + if (out_SlantAngle.D() > SlantAngle_jsonvlaue) { + AlgelFlag = 2; + textPainter.appendPaintJob("盖板上下边缘膜的角度:" + std::to_string(out_SlantAngle.D()), cv::Point(800, 850), cv::Scalar(0, 0, 255)); + } + else + textPainter.appendPaintJob("盖板上下边缘膜的角度:" + std::to_string(out_SlantAngle.D()), cv::Point(800, 850), cv::Scalar(0, 255, 0)); int left = out_ColorType[0].D(); if (left == 2) - textPainter.putText(Paint, "铜", cv::Point(0, 3100), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob("铜", cv::Point(0, 3100), cv::Scalar(0, 0, 255)); else if (left == 1) - textPainter.putText(Paint, "铝", cv::Point(0, 3100), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铝", cv::Point(0, 3100), cv::Scalar(0, 255, 0)); else - textPainter.putText(Paint, "异常", cv::Point(0, 3100), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob("异常", cv::Point(0, 3100), cv::Scalar(0, 0, 255)); int right = out_ColorType[1].D(); if (right == 2) - textPainter.putText(Paint, "铜", cv::Point(Paint.cols - 1000, 3100), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铜", cv::Point(Paint.cols - 1000, 3100), cv::Scalar(0, 255, 0)); else if (right == 1) - textPainter.putText(Paint, "铝", cv::Point(Paint.cols - 1000, 3100), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob("铝", cv::Point(Paint.cols - 1000, 3100), cv::Scalar(0, 0, 255)); else - textPainter.putText(Paint, "异常", cv::Point(Paint.cols - 1000, 3100), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob("异常", cv::Point(Paint.cols - 1000, 3100), cv::Scalar(0, 0, 255)); //结果判定 if (left == 2 || right == 1 || HeightRightFlag == 2 || HeightLeftFlag == 2 || AlgelFlag == 2){ - textPainter.putText(Paint, "NG", cv::Point(100, 150), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob("NG", cv::Point(100, 150), cv::Scalar(0, 0, 255)); plcResult = "2"; data->halconOK = false; } else{ - textPainter.putText(Paint, "OK", cv::Point(100, 150), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("OK", cv::Point(100, 150), cv::Scalar(0, 255, 0)); } } else{ - textPainter.putText(Paint, "NG1", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob("NG1", cv::Point(0, 50), cv::Scalar(0, 0, 255)); plcResult = "2"; } + if (plcResult == "1" && data->service->_paramSyncManager->get("焊前定位", "焊前定位只存NG原图启用", true).toBool())//结果为OK且第二个条件为TRUE时不存图 + data->deleteOriImg(0); data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//发送异常结果 - data->service->_missionResultHandle->toBarChart(data->SNcode, { "焊前NG" + data->SNcode}); + if (flag == 1) + Paint = HObject2Mat(out_DisplayImage); + textPainter.executePaintJobs(Paint); + if (plcResult == "2") { + data->service->_missionResultHandle->toBarChart(data->SNcode, { "焊前NG" }); + data->insertSegOriginImg(sourceImg);//NG时保存原图 + } + else { + data->service->_missionResultHandle->toBarChart(data->SNcode, { }); + } cv::resize(Paint, Paint, cv::Size(), 0.25, 0.25); data->insertResultImg(Paint); @@ -270,7 +277,7 @@ bool ALGO_dingGaiKeMa(std::shared_ptr data, keMaParam* _detParams, sx_ return false; } auto sourceImg = data->getOriImg(0); - switch (address.triggerValue) { + /* switch (address.triggerValue) { case 1: sourceImg = cv::imread("D:\\testImage\\601\\fbf (1).png", -1); break; @@ -280,11 +287,14 @@ bool ALGO_dingGaiKeMa(std::shared_ptr data, keMaParam* _detParams, sx_ case 3: sourceImg = cv::imread("D:\\testImage\\601\\fbf (3).png", -1); break; - } + }*/ if (sourceImg.rows == 100) { cv::Mat Paint; sourceImg.copyTo(Paint); cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送异常结果 + qCritical() << QString::fromStdString(data->cameraKeyName) << QString::fromStdString(data->SNcode) << "相机取图超时"; + data->insertResultImg(Paint); return false; } QByteArray plcResult = "1"; @@ -483,8 +493,9 @@ bool ALGO_dingGaiKeMa(std::shared_ptr data, keMaParam* _detParams, sx_ else data->service->_missionResultHandle->toBarChart(data->SNcode, { }); } - - + //不存储极柱图片 + if ((address.triggerValue == 1 || address.triggerValue == 3) && !data->service->_paramSyncManager->get("刻码", "极柱存图启用", false).toBool()) + data->deleteOriImg(0); return true; } CATCH_EXCEPTIONS diff --git a/src/mission/601/601_RLJ_DingGai/601_RLJ_DingGai.h b/src/mission/601/601_RLJ_DingGai/601_RLJ_DingGai.h index cb7f149..38bfe4f 100644 --- a/src/mission/601/601_RLJ_DingGai/601_RLJ_DingGai.h +++ b/src/mission/601/601_RLJ_DingGai/601_RLJ_DingGai.h @@ -14,7 +14,7 @@ struct hanQianDingWeiSubresult { double Out_Vale_L_centerUp = -1;//铝(负)极连接片中心的和上极耳水平边缘距离(mm) double Out_Vale_L_centerDown = -1;//铝(负)极连接片中心的和下极耳水平边缘距离(mm) double extremumValue = -1; - + cv::Point circleCenter; double radius = 1; std::vector lines; diff --git a/src/mission/601/601_RLJ_DingGai/CMakeLists.txt b/src/mission/601/601_RLJ_DingGai/CMakeLists.txt index 599e8f5..76e9200 100644 --- a/src/mission/601/601_RLJ_DingGai/CMakeLists.txt +++ b/src/mission/601/601_RLJ_DingGai/CMakeLists.txt @@ -40,8 +40,8 @@ FILE(GLOB UI_FILES "./*.ui") #find_package(Qt5 COMPONENTS Core REQUIRED) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Sql REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) add_library(${CURRENT_FOLDER} SHARED ${HEAD_FILES} @@ -64,7 +64,6 @@ target_link_libraries(${CURRENT_FOLDER} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Sql ) diff --git a/src/mission/601/601_RLJ_HH/601_RLJ_HH.cpp b/src/mission/601/601_RLJ_HH/601_RLJ_HH.cpp index cd962ec..c51f69d 100644 --- a/src/mission/601/601_RLJ_HH/601_RLJ_HH.cpp +++ b/src/mission/601/601_RLJ_HH/601_RLJ_HH.cpp @@ -1,6 +1,6 @@ #include <601_RLJ_HH.h> -void halconEvaluate(cv::Mat sourceImg, cv::Mat segImg, TextRendererZH* textPainter, bool& isNg, QByteArray& hanHouTongXingDu, int times, std::shared_ptr data, hanHou2dParam* _detParams) { +void halconEvaluate(cv::Mat sourceImg, cv::Mat segImg, cvPainter* textPainter, bool& isNg, QByteArray& hanHouTongXingDu, int times, std::shared_ptr data, hanHou2dParam* _detParams) { HDevEngineCpp::HDevProcedure proc("Cal_HY_CC_Distance_3_1"); HDevEngineCpp::HDevProcedureCall procedureCall(proc); HObject in_Image = MatToHObject(sourceImg); @@ -36,7 +36,7 @@ void halconEvaluate(cv::Mat sourceImg, cv::Mat segImg, TextRendererZH* textPaint isNg = true; } } -void HandleAIEvaluate(cv::Mat sourceImg, TextRendererZH* textPainter, bool& isNg, int times, std::shared_ptr data, handdleAI::pSegmentation*seg,handdleAI::pClassification* cls) { +void HandleAIEvaluate(cv::Mat sourceImg, cvPainter* textPainter, bool& isNg, int times, std::shared_ptr data, handdleAI::pSegmentation*seg,handdleAI::pClassification* cls) { isNg = false; cv::Mat forSeg; cv::resize(sourceImg, forSeg, cv::Size(), 0.5, 0.5); @@ -109,7 +109,7 @@ bool hanhou2D(std::shared_ptr data, hanHou2dParam* _detParams, sx_581_ data->insertSegOriginImg(seg2); data->insertSegOriginImg(forSeg2); qDebug() << "h start"; - TextRendererZH textPainter1(Paint1.cols, Paint1.rows, 400), textPainter2(Paint1.cols, Paint1.rows, 400); + cvPainter textPainter1, textPainter2; auto futrue1 = std::async(std::launch::async, &halconEvaluate, img2D1, seg1, &textPainter1, std::ref(isNg1), std::ref(hanHouTongXingDu1), 0, data, _detParams); auto futrue2 = std::async(std::launch::async, &halconEvaluate, diff --git a/src/mission/601/601_ZH_Dibu/601_ZH_Dibu.cpp b/src/mission/601/601_ZH_Dibu/601_ZH_Dibu.cpp index bdb74f7..5205187 100644 --- a/src/mission/601/601_ZH_Dibu/601_ZH_Dibu.cpp +++ b/src/mission/601/601_ZH_Dibu/601_ZH_Dibu.cpp @@ -1,10 +1,10 @@ #include <601_ZH_Dibu.h> -bool hanQianCeLiang_Main(std::shared_ptr data, zhongHanHanQianParams* _detParams, sx_581_address address) { +bool hanQianCeLiang_Main(std::shared_ptr data, zhongHanHanQianParams* _detParams, sx_581_address address) { try { QByteArray plcResult = "1"; //cv::Mat sourceImg = cv::imread("D:\\testImage\\T.bmp", -1); cv::Mat sourceImg = data->getOriImg(0); - sourceImg = cv::imread("D:\\testimg\\ORI0_60A01MB5601F8PJ009974_60A01MB5601F8PJ010051_DA3442418_0_01-08-48-98.png", -1); + //sourceImg = cv::imread("D:\\testimg\\ORI0_60A01MB5601F8PJ009974_60A01MB5601F8PJ010051_DA3442418_0_01-08-48-98.png", -1); cv::Mat Paint; sourceImg.copyTo(Paint); if (Paint.channels() == 1) @@ -77,8 +77,9 @@ bool hanQianCeLiang_Main(std::shared_ptr data, zhongHanHanQianParams* text = text.substr(0, text.find(".") + 4) + "mm"; textPainter.putText(Paint, text, cv::Point(50, 550), cv::Scalar(0, 255, 0)); } - double Vale_T_cenretUp = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铜极中心和上边缘距离差(下限)", 1).toDouble(); - if ((13 - Vale_T_cenretUp) < Out_Vale_T_cenretUp && Out_Vale_T_cenretUp < (13 + Vale_T_cenretUp)) + double Vale_T_cenretUp_Max = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铜极中心和上边缘距离差(上限)", 16).toDouble(); + double Vale_T_cenretUp_Min = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铜极中心和上边缘距离差(下限)", 10).toDouble(); + if (Vale_T_cenretUp_Min < Out_Vale_T_cenretUp && Out_Vale_T_cenretUp < Vale_T_cenretUp_Max) { std::string text = " 铜极中心和上边缘距离:" + std::to_string(Out_Vale_T_cenretUp); text = text.substr(0, text.find(".") + 4) + "mm"; @@ -92,9 +93,9 @@ bool hanQianCeLiang_Main(std::shared_ptr data, zhongHanHanQianParams* data->halconOK = false; plcResult = "2"; } - - double Vale_T_cenretDown = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铜极中心和下边缘距离差(下限)", 1).toDouble(); - if ((13 - Vale_T_cenretDown) < Out_Vale_T_centerDown && Out_Vale_T_centerDown < (13 + Vale_T_cenretDown)) + double Vale_T_cenretDown_Max = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铜极中心和下边缘距离差(上限)", 16).toDouble(); + double Vale_T_cenretDown_Min = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铜极中心和下边缘距离差(下限)", 10).toDouble(); + if (Vale_T_cenretDown_Min < Out_Vale_T_centerDown && Out_Vale_T_centerDown < Vale_T_cenretDown_Max) { std::string text = " 铜极中心和下边缘距离:" + std::to_string(Out_Vale_T_centerDown); text = text.substr(0, text.find(".") + 4) + "mm"; @@ -124,9 +125,9 @@ bool hanQianCeLiang_Main(std::shared_ptr data, zhongHanHanQianParams* textPainter.putText(Paint, "NG", cv::Point(0, 50), cv::Scalar(0, 255, 0)); data->halconOK = false; } + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + xinBaoJianJu + "," + jiPianJianJu);//结果反馈 data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 - cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); data->insertResultImg(Paint); return true; @@ -153,7 +154,7 @@ bool hanQianCeLiang_Sub(std::shared_ptr data, zhongHanHanQianParams* _ //cv::Mat sourceImg = cv::imread("D:\\testImage\\L.bmp", -1); cv::Mat sourceImg = data->getOriImg(0); - sourceImg = cv::imread("D:\\testimg\\ORI0_60A01MB5601F8PJ009974_60A01MB5601F8PJ010051_DA3273535_0_01-08-48-98.png", -1); + //sourceImg = cv::imread("D:\\testimg\\ORI0_60A01MB5601F8PJ009974_60A01MB5601F8PJ010051_DA3273535_0_01-08-48-98.png", -1); cv::Mat Paint; sourceImg.copyTo(Paint); if (Paint.channels() == 1) @@ -163,7 +164,7 @@ bool hanQianCeLiang_Sub(std::shared_ptr data, zhongHanHanQianParams* _ //发送图片 _detParams->insertImg(image); - TextRendererZH textPainter(Paint.cols, Paint.rows, 120); + TextRendererZH textPainter(Paint.cols, Paint.rows, 280); //等待结果 hanQianDingWeiSubresult _calResult; bool waitResultResult = _detParams->waitResult(_calResult); @@ -191,8 +192,9 @@ bool hanQianCeLiang_Sub(std::shared_ptr data, zhongHanHanQianParams* _ text = text.substr(0, text.find(".") + 4) + "mm"; textPainter.putText(Paint, text, cv::Point(50, 550), cv::Scalar(0, 255, 0)); } - double Vale_T_cenretUp = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铝极中心和上边缘距离差(下限)", 1).toDouble(); - if ((13 - Vale_T_cenretUp) < _calResult.Out_Vale_L_centerUp && _calResult.Out_Vale_L_centerUp < (13 + Vale_T_cenretUp)) + double Vale_T_cenretUp_Max = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铝极中心和上边缘距离差(上限)", 16).toDouble(); + double Vale_T_cenretUp_Min = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铝极中心和上边缘距离差(下限)", 10).toDouble(); + if (Vale_T_cenretUp_Min < _calResult.Out_Vale_L_centerUp && _calResult.Out_Vale_L_centerUp < Vale_T_cenretUp_Max) { std::string text = " 铜极中心和上边缘距离:" + std::to_string(_calResult.Out_Vale_L_centerUp); text = text.substr(0, text.find(".") + 4) + "mm"; @@ -207,8 +209,9 @@ bool hanQianCeLiang_Sub(std::shared_ptr data, zhongHanHanQianParams* _ plcResult = "2"; } - double Vale_T_cenretDown = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铝极中心和下边缘距离差(下限)", 1).toDouble(); - if ((13 - Vale_T_cenretDown) < _calResult.Out_Vale_L_centerDown && _calResult.Out_Vale_L_centerDown < (13 + Vale_T_cenretDown)) + double Vale_T_cenretDown_Max = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铝极中心和下边缘距离差(上限)", 16).toDouble(); + double Vale_T_cenretDown_Min = data->service->_paramSyncManager->get("焊前", "终焊焊前检测铝极中心和下边缘距离差(下限)", 10).toDouble(); + if (Vale_T_cenretDown_Min < _calResult.Out_Vale_L_centerDown && _calResult.Out_Vale_L_centerDown < Vale_T_cenretDown_Max) { std::string text = " 铜极中心和下边缘距离:" + std::to_string(_calResult.Out_Vale_L_centerDown); text = text.substr(0, text.find(".") + 4) + "mm"; @@ -223,7 +226,7 @@ bool hanQianCeLiang_Sub(std::shared_ptr data, zhongHanHanQianParams* _ plcResult = "2"; } - for (int i = 0; i < 4; i++) + for (int i = 4; i < 8; i++) { cv::line(Paint, cv::Point(_calResult.lines[i * 4 + 1], _calResult.lines[i * 4]), cv::Point(_calResult.lines[i * 4 + 3], _calResult.lines[i * 4 + 2]), @@ -270,10 +273,10 @@ bool tieJiaoJianCe(std::shared_ptr data, detParams* _detParams, sx_581 return false; } auto sourceImg = data->getOriImg(0); - if(data->cameraKeyName == "Camera3" || data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera7") - sourceImg = cv::imread("D:\\testImg\\ORI0_60A01MB5601FAHC349438_60A01MB5601FAHC349437_DA3273471_0_01-54-42-61.png", -1); - else - sourceImg = cv::imread("D:\\testImg\\ORI0_60A01MB5601FAHC349438_60A01MB5601FAHC349437_DA3442575_0_01-54-42-78.png", -1); + //if(data->cameraKeyName == "Camera3" || data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera7") + // sourceImg = cv::imread("D:\\testImg\\ORI0_60A01MB5601FAHC349438_60A01MB5601FAHC349437_DA3273471_0_01-54-42-61.png", -1); + //else + // sourceImg = cv::imread("D:\\testImg\\ORI0_60A01MB5601FAHC349438_60A01MB5601FAHC349437_DA3442575_0_01-54-42-78.png", -1); if (sourceImg.rows == 100) { cv::Mat Paint; sourceImg.copyTo(Paint); @@ -293,22 +296,24 @@ bool tieJiaoJianCe(std::shared_ptr data, detParams* _detParams, sx_581 in_ProductType[0] = ("Al"); in_ProductType[1] = ("yellow"); //nlohmann::json m_json = readJsonFromPath("./config/ZhongHanConfig.json"); - int color_value = data->service->_paramSyncManager->get("终焊", "贴胶胶纸颜色(1蓝色 2黄色)", 1).toInt(); + int color_value = data->service->_paramSyncManager->get("终焊", "贴胶胶纸颜色(1蓝色 2黄色)", 2).toInt(); if (color_value != 1) in_ProductType[1] = ("yellow"); else in_ProductType[1] = ("blue"); - HalconCpp::HTuple in_Resolution(0.02727); + double resolute = data->service->_paramSyncManager->get("终焊", QString::fromStdString(data->cameraKeyName) + "精度", 0.02727).toDouble(); + HalconCpp::HTuple in_Resolution(resolute); HalconCpp::HTuple in_FilePath = "./halconFiles/"; HalconCpp::HTuple in_DIF_Value = 13; TextRendererZH textPainter(Paint.cols, Paint.rows, 300); - if (data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera7") { + if (data->cameraKeyName == "Camera3" || data->cameraKeyName == "Camera7") { in_ProductType[0] = ("Gu"); textPainter.putText(Paint, "铜", cv::Point(50, 1600), cv::Scalar(0, 0, 255)); } else textPainter.putText(Paint, "铝", cv::Point(50, 1600), cv::Scalar(0, 0, 255)); + in_Image = MatToHObject(sourceImg); procedureCall.SetInputIconicParamObject("in_Image", in_Image);// procedureCall.SetInputCtrlParamTuple("in_ProductType", in_ProductType);// @@ -322,10 +327,21 @@ bool tieJiaoJianCe(std::shared_ptr data, detParams* _detParams, sx_581 out_LinePoint = procedureCall.GetOutputCtrlParamTuple("out_LinePoint"); out_GluePosition = procedureCall.GetOutputCtrlParamTuple("out_GluePosition"); out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); + + double 上边缘和连接片圆心距上限 = data->service->_paramSyncManager->get("终焊", "贴胶检测上边缘和连接片圆心距上限", 20.00).toDouble(); + bool 上边缘和连接片圆心距上限判定启用 = data->service->_paramSyncManager->get("终焊", "贴胶检测上边缘和连接片圆心距上限判定启用", true).toBool(); + double 下边缘和连接片圆心距上限 = data->service->_paramSyncManager->get("终焊", "贴胶检测下边缘和连接片圆心距上限", 20.00).toDouble(); + bool 下边缘和连接片圆心距上限判定启用 = data->service->_paramSyncManager->get("终焊", "贴胶检测下边缘和连接片圆心距上限判定启用", true).toBool(); + + double 上边缘和连接片圆心距下限 = data->service->_paramSyncManager->get("终焊", "贴胶检测上边缘和连接片圆心距下限", 12.00).toDouble(); + bool 上边缘和连接片圆心距下限判定启用 = data->service->_paramSyncManager->get("终焊", "贴胶检测上边缘和连接片圆心距下限判定启用", true).toBool(); + double 下边缘和连接片圆心距下限 = data->service->_paramSyncManager->get("终焊", "贴胶检测下边缘和连接片圆心距下限", 12.00).toDouble(); + bool 下边缘和连接片圆心距下限判定启用 = data->service->_paramSyncManager->get("终焊", "贴胶检测下边缘和连接片圆心距下限判定启用", true).toBool(); + QByteArray plcResult = "1"; if (out_Flag.D() == 1) { double distance1 = out_Distance[0].D(); - if (distance1 <= 12) {//NG + if ((distance1 <= 上边缘和连接片圆心距下限 || distance1 >= 上边缘和连接片圆心距上限) && 上边缘和连接片圆心距下限判定启用 && 上边缘和连接片圆心距上限判定启用) {//NG textPainter.putText(Paint, "上边缘和连接片圆心距:" + std::to_string(distance1), cv::Point(1400, 0), cv::Scalar(0, 0, 255)); plcResult = "2"; } @@ -333,7 +349,7 @@ bool tieJiaoJianCe(std::shared_ptr data, detParams* _detParams, sx_581 textPainter.putText(Paint, "上边缘和连接片圆心距:" + std::to_string(distance1), cv::Point(1400, 0), cv::Scalar(0, 255, 0)); } double distance2 = out_Distance[1].D(); - if (distance2 <= 12) {//NG + if ((distance1 <= 下边缘和连接片圆心距下限 || distance1 >= 下边缘和连接片圆心距上限) && 下边缘和连接片圆心距下限判定启用 && 下边缘和连接片圆心距上限判定启用) {//NG textPainter.putText(Paint, "下边缘和连接片圆心距:" + std::to_string(distance2), cv::Point(1400, 200), cv::Scalar(0, 0, 255)); plcResult = "2"; } @@ -376,9 +392,15 @@ bool tieJiaoJianCe(std::shared_ptr data, detParams* _detParams, sx_581 plcResult = "2"; data->halconOK = false; } - cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//发送结果 + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); data->insertResultImg(Paint); + + + cv::Mat tempImg; + cv::resize(sourceImg, tempImg, cv::Size(), 0.5, 0.5); + data->deleteOriImg(0); + data->insertOriImg(tempImg); return true; } CATCH_EXCEPTIONS @@ -386,7 +408,7 @@ bool tieJiaoJianCe(std::shared_ptr data, detParams* _detParams, sx_581 return false; } -void aiEvaluate(const cv::Mat& input, lieHengParam* _detParams, cv::Mat& Paint, bool& isNG){ +void aiEvaluate(std::shared_ptr data, const cv::Mat& input, lieHengParam* _detParams, cv::Mat& Paint, bool& isNG){ cv::Mat forSeg; //input.copyTo(forSeg); cv::resize(input, forSeg, cv::Size(), 0.25, 0.25); @@ -395,6 +417,7 @@ void aiEvaluate(const cv::Mat& input, lieHengParam* _detParams, cv::Mat& Paint, std::vector>contours; cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); TextRendererZH textPainter(Paint.cols, Paint.rows, 50); + std::vector> clsImgs; for (auto& contour : contours) { cv::Rect rect = cv::boundingRect(contour); rect.x *= 4; rect.y * 4; rect.width *= 4; rect.height *= 4; @@ -402,6 +425,7 @@ void aiEvaluate(const cv::Mat& input, lieHengParam* _detParams, cv::Mat& Paint, continue; cv::Mat clsImg = cutImg2Cls(rect, input); int claResult = _detParams->cls->Evaluate(clsImg); + clsImgs.push_back({ "result" + std::to_string(claResult),clsImg }); switch (claResult) { case 0://OK @@ -414,7 +438,7 @@ void aiEvaluate(const cv::Mat& input, lieHengParam* _detParams, cv::Mat& Paint, break; } } - + data->insertClsOriginImg(clsImgs); } bool diBuLieHeng(std::shared_ptr data, lieHengParam* _detParams, sx_581_address address) { try { @@ -426,7 +450,7 @@ bool diBuLieHeng(std::shared_ptr data, lieHengParam* _detParams, sx_58 return false; } auto oriImgs = data->getOriImgVec(); - oriImgs[0] = cv::imread("D:\\testImg\\ORI1_60A01MB5601FAED757502_DA3442391_0_00-57-18-67.png", -1); + //oriImgs[0] = cv::imread("D:\\testImg\\ORI1_60A01MB5601FAED757502_DA3442391_0_00-57-18-67.png", -1); if (oriImgs[0].rows == 100) { cv::Mat Paint; oriImgs[0].copyTo(Paint); @@ -449,19 +473,23 @@ bool diBuLieHeng(std::shared_ptr data, lieHengParam* _detParams, sx_58 QByteArray plcResult = "1"; bool isNG = false; - aiEvaluate(src1, _detParams,Paint, isNG); + aiEvaluate(data,src1, _detParams,Paint, isNG); if (isNG == true) { textPainter.putText(Paint, "NG", cv::Point(50, 50), cv::Scalar(0, 255, 0)); plcResult = "2"; + data->aiDetOK = false; } else textPainter.putText(Paint, "OK", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); //data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//发送结果 - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + data->insertResultImg(Paint); + + return true; } CATCH_EXCEPTIONS diff --git a/src/mission/601/601_ZH_Dibu/601_ZH_Dibu.h b/src/mission/601/601_ZH_Dibu/601_ZH_Dibu.h index d6564f0..fdf9b25 100644 --- a/src/mission/601/601_ZH_Dibu/601_ZH_Dibu.h +++ b/src/mission/601/601_ZH_Dibu/601_ZH_Dibu.h @@ -138,25 +138,25 @@ public: hanQianDingWei2.init("Camera2", "焊前定位-铝", { { 5090, 1,{5070,5100},"5092","5094",5096} }, 1, &_ZH_HQDW_Param, _serviceManager, bridge, hanQianCeLiang_Sub, "焊前定位CCD"); tieJiao_dibu90_1.init("Camera3", "反面贴胶-相机1", { { 5500, 1,{5530,5480},"5502","5504"} }, 1, &_detParams, _serviceManager, bridge, tieJiaoJianCe, "贴胶检测CCD"); - tieJiao_dibu45_2.init("virtualCamera02", "贴胶底部45_2", { { 5510, 1,{5530,5480},"5512","5514"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); - tieJiao_dibu45_3.init("virtualCamera03", "贴胶底部45_3", { { 5520, 1,{5530,5480},"5522","5524"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); - tieJiao_dibu90_4.init("Camera4", "反面贴胶-相机4", { { 5530, 1,{5530,5480},"5532","5534"} }, 1, &_detParams, _serviceManager, bridge, tieJiaoJianCe, "贴胶检测CCD"); - tieJiao_dibu45_5.init("virtualCamera05", "贴胶底部45_5", { { 5540, 1,{5530,5480},"5542","5544"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); - tieJiao_dibu45_6.init("virtualCamera06", "贴胶底部45_6", { { 5550, 1,{5530,5480},"5552","5554"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //tieJiao_dibu45_2.init("virtualCamera02", "贴胶底部45_2", { { 5510, 1,{5530,5480},"5512","5514"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //tieJiao_dibu45_3.init("virtualCamera03", "贴胶底部45_3", { { 5520, 1,{5530,5480},"5522","5524"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + tieJiao_dibu90_4.init("Camera4", "反面贴胶-相机4", { { 5550, 1,{5530,5480},"5552","5554"} }, 1, &_detParams, _serviceManager, bridge, tieJiaoJianCe, "贴胶检测CCD"); + //tieJiao_dibu45_5.init("virtualCamera05", "贴胶底部45_5", { { 5540, 1,{5530,5480},"5542","5544"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //tieJiao_dibu45_6.init("virtualCamera06", "贴胶底部45_6", { { 5550, 1,{5530,5480},"5552","5554"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); lieHeng_dibu90_1.init("Camera5", "裂痕检测-相机1", { { 5200, 1,{5120,5140},"5202","5204"} }, 1, &_lieHengParam1, _serviceManager, bridge, diBuLieHeng, "底部检测CCD"); - lieHeng_dibu45_2.init("virtualCamera12", "裂痕底部45_2", { { 5210, 1,{5120,5140},"5212","5214"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); - lieHeng_dibu45_3.init("virtualCamera13", "裂痕底部45_3", { { 5220, 1,{5120,5140},"5222","5224"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //lieHeng_dibu45_2.init("virtualCamera12", "裂痕底部45_2", { { 5210, 1,{5120,5140},"5212","5214"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //lieHeng_dibu45_3.init("virtualCamera13", "裂痕底部45_3", { { 5220, 1,{5120,5140},"5222","5224"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); lieHeng_dibu90_4.init("Camera6", "裂痕检测-相机4", { { 5230, 1,{5120,5140},"5232","5234"} }, 1, &_lieHengParam2, _serviceManager, bridge, diBuLieHeng, "底部检测CCD"); - lieHeng_dibu45_5.init("virtualCamera15", "裂痕底部45_5", { { 5240, 1,{5120,5140},"5242","5244"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); - lieHeng_dibu45_6.init("virtualCamera16", "裂痕底部45_6", { { 5250, 1,{5120,5140},"5252","5254"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //lieHeng_dibu45_5.init("virtualCamera15", "裂痕底部45_5", { { 5240, 1,{5120,5140},"5242","5244"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //lieHeng_dibu45_6.init("virtualCamera16", "裂痕底部45_6", { { 5250, 1,{5120,5140},"5252","5254"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); lieXi_dibu90_1.init("Camera7", "正面贴胶-相机1", { { 5280, 1,{5160,5180},"5282","5284"} }, 1, &_detParams, _serviceManager, bridge, tieJiaoJianCe, "贴胶检测CCD"); - lieXi_dibu45_2.init("virtualCamera22", "裂隙底部45_2", { { 5290, 1,{5160,5180},"5292","5294"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); - lieXi_dibu45_3.init("virtualCamera23", "裂隙底部45_3", { { 5300, 1,{5160,5180},"5302","5304"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //lieXi_dibu45_2.init("virtualCamera22", "裂隙底部45_2", { { 5290, 1,{5160,5180},"5292","5294"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //lieXi_dibu45_3.init("virtualCamera23", "裂隙底部45_3", { { 5300, 1,{5160,5180},"5302","5304"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); lieXi_dibu90_4.init("Camera8", "正面贴胶-相机4", { { 5310, 1,{5160,5180},"5312","5314"} }, 1, &_detParams, _serviceManager, bridge, tieJiaoJianCe, "贴胶检测CCD"); - lieXi_dibu45_5.init("virtualCamera25", "裂隙底部45_5", { { 5320, 1,{5160,5180},"5322","5334"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); - lieXi_dibu45_6.init("virtualCamera26", "裂隙底部45_6", { { 5330, 1,{5160,5180},"5332","5334"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //lieXi_dibu45_5.init("virtualCamera25", "裂隙底部45_5", { { 5320, 1,{5160,5180},"5322","5334"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); + //lieXi_dibu45_6.init("virtualCamera26", "裂隙底部45_6", { { 5330, 1,{5160,5180},"5332","5334"} }, 1, &_detParams, _serviceManager, bridge, retrunSignal); #pragma endregion return true; diff --git a/src/mission/601/601_ZH_Dibu/CMakeLists.txt b/src/mission/601/601_ZH_Dibu/CMakeLists.txt index 599e8f5..76e9200 100644 --- a/src/mission/601/601_ZH_Dibu/CMakeLists.txt +++ b/src/mission/601/601_ZH_Dibu/CMakeLists.txt @@ -40,8 +40,8 @@ FILE(GLOB UI_FILES "./*.ui") #find_package(Qt5 COMPONENTS Core REQUIRED) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Sql REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) add_library(${CURRENT_FOLDER} SHARED ${HEAD_FILES} @@ -64,7 +64,6 @@ target_link_libraries(${CURRENT_FOLDER} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Sql ) diff --git a/src/mission/601/601_ZhongHan_hanhou/601_ZhongHan_hanhou.cpp b/src/mission/601/601_ZhongHan_hanhou/601_ZhongHan_hanhou.cpp new file mode 100644 index 0000000..ded7efd --- /dev/null +++ b/src/mission/601/601_ZhongHan_hanhou/601_ZhongHan_hanhou.cpp @@ -0,0 +1,566 @@ +#include <601_ZhongHan_hanhou.h> +//如果在范围内则进行补偿,不在则返回实际值 +cv::Mat cutLabelImg(cv::Mat pic, int W, int H, cv::Rect rect, int edgeType, int largeType, int color) +{ + cv::Point center; + center.x = rect.x + rect.width / 2; + center.y = rect.y + rect.height / 2; + + if (rect.width <= W && rect.height <= H) + { + if (edgeType == 0) + { + cv::Rect rec = { center.x - W / 2,center.y - H / 2,W,H }; + if (rec.x < 0) + rec.x = 0; + if (rec.y < 0) + rec.y = 0; + if (rec.x + rec.width > pic.cols) + rec.x = pic.cols - rec.width; + if (rec.y + rec.height > pic.rows) + rec.y = pic.rows - rec.height; + + cv::Mat picCut; + pic(rec).copyTo(picCut); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + + int x, y; + cv::Rect rec; + x = center.x - W / 2; + y = center.y - H / 2; + rec.x = x; + rec.y = y; + rec.width = W; + rec.height = H; + cv::Mat picCut(H, W, pic.type(), cv::Scalar(color, color, color)); + if (x < 0) + { + rec.width += rec.x; + xDiff = 0 - rec.x; + rec.x = 0; + } + if (y < 0) + { + rec.height += rec.y; + yDiff = 0 - rec.y; + rec.y = 0; + } + if (rec.x + rec.width > pic.cols) + rec.width = pic.cols - rec.x; + if (rec.y + rec.height > pic.rows) + rec.height = pic.rows - rec.y; + + pic(rec).copyTo(picCut({ xDiff, yDiff, rec.width, rec.height })); + return picCut; + } + } + else + { + if (largeType == 0) + { + int xDiff = 0; + int yDiff = 0; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rect.x < 0) + { + rect.width += rect.x; + rect.x = 0; + } + if (rect.y < 0) + { + rect.height += rect.y; + rect.y = 0; + } + if (rect.x + rect.width > pic.cols) + rect.width = pic.cols - rect.x; + if (rect.y + rect.height > pic.rows) + rect.height = pic.rows - rect.y; + + xDiff = (wTem - rect.width) * 1.0 / 2; + yDiff = (hTem - rect.height) * 1.0 / 2; + + pic(rect).copyTo(picCut({ xDiff, yDiff, rect.width, rect.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + cv::Rect rectTem; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + rectTem = { rect.x + rect.width / 2 - wTem / 2,rect.y + rect.height / 2 - hTem / 2,wTem,hTem }; + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rectTem.x < 0) + { + rectTem.width += rectTem.x; + xDiff = 0 - rectTem.x; + rectTem.x = 0; + } + if (rectTem.y < 0) + { + rectTem.height += rectTem.y; + yDiff = 0 - rectTem.y; + rectTem.y = 0; + } + if (rectTem.x + rectTem.width > pic.cols) + rectTem.width = pic.cols - rectTem.x; + if (rectTem.y + rectTem.height > pic.rows) + rectTem.height = pic.rows - rectTem.y; + + pic(rectTem).copyTo(picCut({ xDiff, yDiff, rectTem.width, rectTem.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + } + + return cv::Mat(); +} +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + TextRendererZH textPainter(Paint.cols, Paint.rows, textSize); + textPainter.putText(Paint, "SN:" + sn, cv::Point(0, Paint.rows - textSize * 2), cv::Scalar(0, 255, 0)); + textPainter.putText(Paint, QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); +} +void exceptionResume(std::shared_ptr data, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + +} + +bool zhongHan_hanHouCeLiang(std::shared_ptr data, zhongHan2dCeLiangParams* _detParams, sx_581_address address) { + try { + auto detectStartTime = std::chrono::high_resolution_clock::now(); + +#pragma region 获取参数 + double pixAcc = data->service->_paramSyncManager->get("焊后检测", QString::fromStdString(data->cameraKeyName + "相机精度"), 0.0316699).toDouble(); + QString type = "铝"; + if (_detParams->isTong == true) + type = "铜"; + valueControlStruct LT_水平间距 = genValueControlStruct("焊后测量-" + type, "左上水平间距", "mm", 20.0, 15.0, false, data->service->_paramSyncManager); + valueControlStruct LT_竖直间距 = genValueControlStruct("焊后测量-" + type, "左上竖直间距", "mm", 16.0, 10.0, false, data->service->_paramSyncManager); + valueControlStruct LT_到裁切边距离 = genValueControlStruct("焊后测量-" + type, "左上到裁切边距离", "mm", 5.0, 0.0, false, data->service->_paramSyncManager); + + valueControlStruct RT_水平间距 = genValueControlStruct("焊后测量-" + type, "右上水平间距", "mm", 20.0, 15.0, false, data->service->_paramSyncManager); + valueControlStruct RT_竖直间距 = genValueControlStruct("焊后测量-" + type, "右上竖直间距", "mm", 16.0, 10.0, false, data->service->_paramSyncManager); + valueControlStruct RT_到裁切边距离 = genValueControlStruct("焊后测量-" + type, "右上到裁切边距离", "mm", 5.0, 0.0, false, data->service->_paramSyncManager); + + valueControlStruct LB_水平间距 = genValueControlStruct("焊后测量-" + type, "左下水平间距", "mm", 20.0, 15.0, false, data->service->_paramSyncManager); + valueControlStruct LB_竖直间距 = genValueControlStruct("焊后测量-" + type, "左下竖直间距", "mm", 16.0, 10.0, false, data->service->_paramSyncManager); + valueControlStruct LB_到裁切边距离 = genValueControlStruct("焊后测量-" + type, "左下到裁切边距离", "mm", 5.0, 0.0, false, data->service->_paramSyncManager); + + valueControlStruct RB_水平间距 = genValueControlStruct("焊后测量-" + type, "右下水平间距", "mm", 20.0, 15.0, false, data->service->_paramSyncManager); + valueControlStruct RB_竖直间距 = genValueControlStruct("焊后测量-" + type, "右下竖直间距", "mm", 16.0, 10.0, false, data->service->_paramSyncManager); + valueControlStruct RB_到裁切边距离 = genValueControlStruct("焊后测量-" + type, "右下到裁切边距离", "mm", 5.0, 0.0, false, data->service->_paramSyncManager); +#pragma endregion + QByteArray plcResult = "1"; + + auto getParamEndTime = std::chrono::high_resolution_clock::now(); + + cv::Mat sourceImg = data->getOriImg(0); +#if 0 + if (_detParams->isTong == true) + sourceImg = cv::imread("D:\\testImgs\\T.bmp", -1); + else + sourceImg = cv::imread("D:\\testImgs\\L.bmp", -1); +#endif + cv::Mat segProcessMat; + float resizeRate = 5.0; + cv::resize(sourceImg, segProcessMat, cv::Size(), 1.0 / resizeRate, 1.0 / resizeRate); + cv::Mat segResult = _detParams->seg_hanHouDingWei->Evaluate(segProcessMat); + + halconInput input; + input.in_Image = MatToHObject(sourceImg); + input.in_ImageSeg = MatToHObject(segResult); + input.in_Resolution = pixAcc; + auto output = _detParams->hEvaluater->evaluate(input); + if (!output.runFlag) { + std::cout << output.faildResult << std::endl; + exceptionResume(data, address, "算法错误:" + QString::fromStdString(output.faildResult)); + return false; + } + std::vectordefects; + cvPainter textPainter(200), textPainterLarge(300);; + if (output.result.out_Flag.D() == 1) { + { + int out_LineCor_L = output.result.out_LineCor.Length(); + for (auto i = 0; i < out_LineCor_L / 4; i++) { + textPainter.appendPaintJob(cv::Point(output.result.out_LineCor[i * 4 + 1].D(), output.result.out_LineCor[i * 4].D()), + cv::Point(output.result.out_LineCor[i * 4 + 3].D(), output.result.out_LineCor[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 20); + } + } + { + int out_BaseLineCor_L = output.result.out_BaseLineCor.Length(); + for (auto i = 0; i < out_BaseLineCor_L / 4; i++) { + textPainter.appendPaintJob(cv::Point(output.result.out_BaseLineCor[i * 4].D(), output.result.out_BaseLineCor[i * 4 + 1].D()), + cv::Point(output.result.out_BaseLineCor[i * 4 + 2].D(), output.result.out_BaseLineCor[i * 4 + 3].D()), + cv::Scalar(0, 255, 0), 20); + } + } + //1 + { + valueJudgement(output.result.out_Distance1[0].D(), + LT_水平间距.maxThreshold, LT_水平间距.minThreshold, 0.5, LT_水平间距.enable, + data->halconOK, "水平间距", "mm", "左上水平间距NG", cv::Point(50, 25), defects, textPainter); + valueJudgement(output.result.out_Distance1[1].D(), + LT_竖直间距.maxThreshold, LT_竖直间距.minThreshold, 0.5, LT_竖直间距.enable, + data->halconOK, "竖直间距", "mm", "左上竖直间距NG", cv::Point(50, 225), defects, textPainter); + valueJudgement(output.result.out_Distance1[2].D(), + LT_到裁切边距离.maxThreshold, LT_到裁切边距离.minThreshold, 0.5, LT_到裁切边距离.enable, + data->halconOK, "到裁切边距离", "mm", "左上到裁切边距离距NG", cv::Point(50, 425), defects, textPainter); + data->sqlVec.insert({ "左上水平间距",output.result.out_Distance1[0].D() }); + data->sqlVec.insert({ "左上竖直间距",output.result.out_Distance1[1].D() }); + data->sqlVec.insert({ "左上到裁切边距离",output.result.out_Distance1[2].D() }); + } + //2 右上 + { + valueJudgement(output.result.out_Distance2[0].D(), + RT_水平间距.maxThreshold, RT_水平间距.minThreshold, 0.5, RT_水平间距.enable, + data->halconOK, "水平间距", "mm", "右上水平间距NG", cv::Point(3500, 25), defects, textPainter); + valueJudgement(output.result.out_Distance2[1].D(), + RT_竖直间距.maxThreshold, RT_竖直间距.minThreshold, 0.5, RT_竖直间距.enable, + data->halconOK, "竖直间距", "mm", "右上竖直间距NG", cv::Point(3500, 225), defects, textPainter); + valueJudgement(output.result.out_Distance2[2].D(), + RT_到裁切边距离.maxThreshold, RT_到裁切边距离.minThreshold, 0.5, RT_到裁切边距离.enable, + data->halconOK, "到裁切边距离", "mm", "右上到裁切边距离NG", cv::Point(3500, 425), defects, textPainter); + + data->sqlVec.insert({ "右上水平间距",output.result.out_Distance2[0].D() }); + data->sqlVec.insert({ "右上竖直间距",output.result.out_Distance2[1].D() }); + data->sqlVec.insert({ "右上到裁切边距离",output.result.out_Distance2[2].D() }); + } + + //3 左下 + { + valueJudgement(output.result.out_Distance3[0].D(), + LB_水平间距.maxThreshold, LB_水平间距.minThreshold, 0.5, LB_水平间距.enable, + data->halconOK, "水平间距", "mm", "左下水平间距NG", cv::Point(50, 3000), defects, textPainter); + valueJudgement(output.result.out_Distance3[1].D(), + LB_竖直间距.maxThreshold, LB_竖直间距.minThreshold, 0.5, LB_竖直间距.enable, + data->halconOK, "竖直间距", "mm", "左下竖直间距NG", cv::Point(50, 3200), defects, textPainter); + valueJudgement(output.result.out_Distance3[2].D(), + LB_到裁切边距离.maxThreshold, LB_到裁切边距离.minThreshold, 0.5, LB_到裁切边距离.enable, + data->halconOK, "到裁切边距离", "mm", "左下到裁切边距离NG", cv::Point(50, 3400), defects, textPainter); + + data->sqlVec.insert({ "左下水平间距",output.result.out_Distance3[0].D() }); + data->sqlVec.insert({ "左下竖直间距",output.result.out_Distance3[1].D() }); + data->sqlVec.insert({ "左下到裁切边距离",output.result.out_Distance3[2].D() }); + } + + //4 右下 + { + valueJudgement(output.result.out_Distance4[0].D(), + RB_水平间距.maxThreshold, RB_水平间距.minThreshold, 0.5, RB_水平间距.enable, + data->halconOK, "水平间距", "mm", "右下水平间距NG", cv::Point(3500, 3000), defects, textPainter); + valueJudgement(output.result.out_Distance4[1].D(), + RB_竖直间距.maxThreshold, RB_竖直间距.minThreshold, 0.5, RB_竖直间距.enable, + data->halconOK, "竖直间距", "mm", "右下竖直间距NG", cv::Point(3500, 3200), defects, textPainter); + valueJudgement(output.result.out_Distance4[2].D(), + RB_到裁切边距离.maxThreshold, RB_到裁切边距离.minThreshold, 0.5, RB_到裁切边距离.enable, + data->halconOK, "到裁切边距离", "mm", "右下到裁切边距离NG", cv::Point(3500, 3400), defects, textPainter); + + data->sqlVec.insert({ "右下水平间距",output.result.out_Distance4[0].D() }); + data->sqlVec.insert({ "右下竖直间距",output.result.out_Distance4[1].D() }); + data->sqlVec.insert({ "右下到裁切边距离",output.result.out_Distance4[2].D() }); + } + + } + else { + defects.push_back("焊印测量NG"); + textPainterLarge.appendPaintJob("焊印测量NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + data->halconOK = false; + plcResult = "2"; + } + plcResult = "1";//强制OK + + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1";//强制OK + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + textPainterLarge.executePaintJobs(Paint); + textPainter.executePaintJobs(Paint); + putSnAndDetTime(Paint, data->SNcode, 80); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->insertSegOriginImg(segProcessMat); + data->insertSegOriginImg(segResult); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "ERROR"); + return true; +} +//在一张图上进行缺陷Seg和Cls的判定 +std::vector> aiEvaluate(cv::Mat input, cv::Mat& Paint, bool& fanZheIsNg, bool& siLieIsNg, zhongHan2dCeMianParams* _detParams) +{ + std::vector> clsImgs; + cv::Mat segRes = ~_detParams->seg_ceMian->Evaluate(input); + std::vector>contours; + cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + int clsIndex = 0; + TextRendererZH textPainter(Paint.cols, Paint.rows, 50); + for (auto& contour : contours) { + clsIndex++; + cv::Rect rect = cv::boundingRect(contour); + if (rect.width < 200 || rect.height < 200) + continue; + + cv::RotatedRect rRect = cv::minAreaRect(contour); + float rWidth = rRect.size.width; + float rHeight = rRect.size.height; + if (rWidth > rHeight) { + float tempLength = rWidth; + rWidth = rHeight; + rHeight = rWidth; + } + + std::vector< cv::Mat> cutClsImages; + if (rHeight / rWidth > 10) { + cutClsImages = cutImg2ClsVec(rect, input); + } + else { + cv::Mat clsImg = cutLabelImg(input, 128, 128, rect, 1, 1, 0); + cutClsImages.push_back(clsImg); + } + + for (auto& clsImg : cutClsImages) { + auto claResult = _detParams->cls_ceMian->Evaluate(clsImg); + clsImgs.push_back({ std::to_string(clsIndex), clsImg }); + switch (claResult.classes) { + case 0://OK + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 3); + break; + case 1://NG + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + cv::drawContours(Paint, contours, clsIndex - 1, cv::Scalar(0, 0, 255), 2); + textPainter.putText(Paint, "撕裂", cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255)); + siLieIsNg = true; + break; + case 2:// + textPainter.putText(Paint, "翻折", cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255)); + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + fanZheIsNg = true; + break; + } + } + } + return clsImgs; +} + +bool zhongHan_hanHouCeMian(std::shared_ptr data, zhongHan2dCeMianParams* _detParams, sx_581_address address) +{ + QByteArray plcResult = "1"; + + try { + bool siLieEnable = data->service->_paramSyncManager->get("焊后45°模型", "撕裂NG启用", true).toBool(); + bool fanZheEnable = data->service->_paramSyncManager->get("焊后45°模型", "翻折NG启用", true).toBool(); + double dingWeiResizeRate = data->service->_paramSyncManager->get("焊后45°模型", "极耳定位模型缩放倍率", 10.0).toDouble(); + double resizeRateAfterCut = data->service->_paramSyncManager->get("焊后45°模型", "裁切后缩放比例", 0.4).toDouble(); + cv::Mat segImage, cutImage, img2D = data->getOriImg(0); + + //img2D = cv::imread("D:/testImgs/45.bmp", -1); + if(dingWeiResizeRate != 1.0) + cv::resize(img2D, segImage, cv::Size(), 1 / dingWeiResizeRate, 1 / dingWeiResizeRate); + segImage = ~_detParams->seg_JiErDIngWei->Evaluate(segImage); + std::vector> contours; + cv::findContours(segImage, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + if (contours.size() != 0) { + int maxIndex = 0; + double maxArea = cv::contourArea(contours[0]); + for (int i = 0; i < contours.size(); i++) { + double area = cv::contourArea(contours[i]); + if (area > maxArea) { + maxIndex = i; + maxArea = area; + } + } + cv::Rect ROI = cv::boundingRect(contours[maxIndex]); + ROI.x = ROI.x * dingWeiResizeRate - 30; + ROI.y = ROI.y * dingWeiResizeRate - 30; + ROI.width = ROI.width * dingWeiResizeRate + 60; + ROI.height = ROI.height * dingWeiResizeRate + 60; + ROI = ROI & (cv::Rect(0, 0, img2D.cols, img2D.rows)); + img2D(ROI).copyTo(cutImage); + cv::resize(cutImage, cutImage, cv::Size(), resizeRateAfterCut, resizeRateAfterCut); + } + + cv::Mat Paint; + cutImage.copyTo(Paint); + putSnAndDetTime(Paint, data->SNcode, 40); + bool fanZheIsNg = false, siLieIsNg = false; + + std::vector> clsImgs; + clsImgs.resize(0); + + clsImgs = aiEvaluate(cutImage, Paint, fanZheIsNg, siLieIsNg, _detParams); + TextRendererZH textPainter(Paint.cols, Paint.rows, 120); + + if (fanZheIsNg == true || siLieIsNg == true) { + std::string ngText; + if (fanZheIsNg == true)ngText += "翻折"; + if (siLieIsNg == true)ngText += "撕裂"; + textPainter.putText(Paint, ngText + "NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + //plcResult = "1"; + } + else { + textPainter.putText(Paint, "OK", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + } + data->insertClsOriginImg(clsImgs); + data->insertResultImg(Paint); + data->insertSegOriginImg(cutImage); + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "ERROR"); + return true; + +} + +halconResult halconProcess(const cv::Mat& imageH, const cv::Mat& imageG, int cnt, handdleAI::pSegmentation* ptr) +{ + halconResult hResult; + imageG.copyTo(hResult.Paint); + if (hResult.Paint.channels() == 1) + cvtColor(hResult.Paint, hResult.Paint, cv::COLOR_GRAY2BGR); + try + { + HDevEngineCpp::HDevProcedure proc("_2_FinalWelding_HY_Height"); + HDevProcedureCall procedureCall(proc); + + HObject in_ImageH, in_ImageG, in_ImageSeg; + + in_ImageH = MatToHObject(imageH); + in_ImageG = MatToHObject(imageG); + hResult.segResult = ptr->Evaluate(imageG); + in_ImageSeg = MatToHObject(hResult.segResult); + procedureCall.SetInputIconicParamObject("in_ImageH", in_ImageH); + procedureCall.SetInputIconicParamObject("in_ImageG", in_ImageG); + procedureCall.SetInputIconicParamObject("in_ImageSeg", in_ImageSeg); + + + procedureCall.SetInputCtrlParamTuple("in_xResolution", 0.004); + procedureCall.SetInputCtrlParamTuple("in_yResolution", 0.0125); + procedureCall.SetInputCtrlParamTuple("in_zResolution", 0.0008); + procedureCall.Execute();//执行halcon程序 + + HTuple out_HYNumber = procedureCall.GetOutputCtrlParamTuple("out_HYNumber");//获取输出控制类变量 + HTuple out_HYPointX = procedureCall.GetOutputCtrlParamTuple("out_HYPointX");//获取输出控制类变量 + HTuple out_HYPointY = procedureCall.GetOutputCtrlParamTuple("out_HYPointY");//获取输出控制类变量 + HTuple out_HYHeightValue = procedureCall.GetOutputCtrlParamTuple("out_HYHeightValue");//获取输出控制类变量 + HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag");//获取输出图像类变量 + HTuple out_NgDetails = procedureCall.GetOutputCtrlParamTuple("out_NgDetails");//获取输出图像类变量 + + int flag = out_Flag.D(); + TextRendererZH textPainter(hResult.Paint.cols, hResult.Paint.rows, 120); + if (flag == 1) { + hResult.hyCount = (int)out_HYNumber[0].D() + (int)out_HYNumber[1].D(); + textPainter.putText(hResult.Paint, "上焊印数量:" + std::to_string((int)out_HYNumber[0].D()), cv::Point(0, 50), cv::Scalar(0, 255, 0)); + textPainter.putText(hResult.Paint, "下焊印数量:" + std::to_string((int)out_HYNumber[1].D()), cv::Point(0, 150), cv::Scalar(0, 255, 0)); + + hResult.data.topHanYinCount = (int)out_HYNumber[0].D(); + hResult.data.bottomHanYinCount = (int)out_HYNumber[1].D(); + std::vector data; + for (int i = 0; i < out_HYPointX.Length() && i < out_HYPointY.Length() && i < out_HYHeightValue.Length(); i++) + { + Cross(hResult.Paint, cv::Point(out_HYPointX[i].D(), out_HYPointY[i].D()), 5, 2, cv::Scalar(0, 255, 0)); + putText(hResult.Paint, + std::to_string(out_HYHeightValue[i].D()), + cv::Point(out_HYPointX[i].D() - 30, out_HYPointY[i].D() - 15), + 1, 1, cv::Scalar(0, 255, 0), 1); + data.push_back(out_HYHeightValue[i].D()); + } + + } + else { + textPainter.putText(hResult.Paint, out_NgDetails.ToString().Text(), cv::Point(0, 250), cv::Scalar(0, 255, 0)); + hResult.result = false; + } + } + CATCH_EXCEPTIONS + return hResult; +} +bool zhongHan_hanHou3D(std::shared_ptr data, zhongHan3DParams* _detParams, sx_581_address address) +{ + try + { + //data->getOriImg(0) = cv::imread("D:\\testImage\\G.tiff", -1); + //data->oriImgs[1] = cv::imread("D:\\testImage\\H.tiff", -1); + //cv::Mat imageG = data->getOriImg(0), imageH = data->getOriImg(1); + //halconResult result1 = halconProcess(imageH, imageG, 0, _detParams->seg_3dHanYinDingWei); + //data->insertSegOriginImg(result1.segResult); + + //data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_1");//结果反馈 + //data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 1) + "_1");//结果反馈 + + //cv::Mat PaintSum(result1.Paint.rows, result1.Paint.cols + result2.Paint.cols, CV_8UC3); + //result1.Paint.copyTo(PaintSum(cv::Rect(0, 0, result1.Paint.cols, result1.Paint.rows))); + //result2.Paint.copyTo(PaintSum(cv::Rect(result1.Paint.cols, 0, result1.Paint.cols, result1.Paint.rows))); + //data->insertResultImg(result1.Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "ERROR"); + return true; + +} + +mission* createMission() { + return new _604_ZhongHan_hanhou(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/601/601_ZhongHan_hanhou/601_ZhongHan_hanhou.h b/src/mission/601/601_ZhongHan_hanhou/601_ZhongHan_hanhou.h new file mode 100644 index 0000000..7b06f9a --- /dev/null +++ b/src/mission/601/601_ZhongHan_hanhou/601_ZhongHan_hanhou.h @@ -0,0 +1,156 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +struct halconInput { + HObject in_Image; + HObject in_ImageSeg; + HTuple in_Resolution; +}; +struct halconOutput { + HTuple out_BaseLineCor, out_LineCor, + out_Distance1, out_Distance2, out_Distance3, out_Distance4, + out_ColorType, out_Flag; +}; +class zhongHan2dCeLiangParams { +public: + std::shared_ptr seg_hanHouDingWei = nullptr; + std::shared_ptr> hEvaluater; + bool isTong = true; + zhongHan2dCeLiangParams() { + hEvaluater = std::make_shared>(); + hEvaluater->init("_2_Cal_Welding_Distance", + [](std::map& inputHObj, + std::map& inputHTuple, + const halconInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHObj["in_ImageSeg"] = in.in_ImageSeg; + inputHTuple["in_Resolution"] = in.in_Resolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + halconOutput& out) { + out.out_BaseLineCor = outputHTuple["out_BaseLineCor"]; + out.out_LineCor = outputHTuple["out_LineCor"]; + out.out_Distance1 = outputHTuple["out_Distance1"]; + out.out_Distance2 = outputHTuple["out_Distance2"]; + out.out_Distance3 = outputHTuple["out_Distance3"]; + out.out_Distance4 = outputHTuple["out_Distance4"]; + out.out_ColorType = outputHTuple["out_ColorType"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + } +}; +class zhongHan2dCeMianParams { +public: + std::shared_ptr seg_JiErDIngWei = nullptr; + std::shared_ptr seg_ceMian = nullptr; + std::shared_ptr cls_ceMian = nullptr; + bool isTong = true; +}; +class zhongHan3DParams { +public: + std::shared_ptr seg_3dHanYinDingWei = nullptr; + bool isTong = true; +}; + +struct halconResult { + struct halconData { + int topHanYinCount = 0; + int bottomHanYinCount = 0; + double avgHanYinDepth = 0; + double maxHanYinDepth = 0; + double minHanYinDepth = 0; + bool detResultIsOk = true; + }; + cv::Mat Paint; + int hyCount; + cv::Mat segResult; + bool result; + halconData data; +}; + +bool zhongHan_hanHouCeLiang(std::shared_ptr data, zhongHan2dCeLiangParams* _detParams, sx_581_address address); +std::vector> aiEvaluate(cv::Mat input, cv::Mat& Paint, bool& isNG, zhongHan2dCeMianParams* _detParams); +bool zhongHan_hanHouCeMian(std::shared_ptr data, zhongHan2dCeMianParams* _detParams, sx_581_address address); +halconResult halconProcess(const cv::Mat& imageH, const cv::Mat& imageG, int cnt, handdleAI::pSegmentation* ptr); +bool zhongHan_hanHou3D(std::shared_ptr data, zhongHan3DParams* _detParams, sx_581_address address); + +class _604_ZhongHan_hanhou : public mission { +public: + _581_SX_Single_triggerTemplate< zhongHan2dCeLiangParams> lv_top_1; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> lv_left45_2; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> lv_right45_3; + _581_SX_Single_triggerTemplate< zhongHan3DParams> _3D_1; + + _581_SX_Single_triggerTemplate< zhongHan2dCeLiangParams> tong_top_5; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> tong_left45_6; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> tong_right45_7; + _581_SX_Single_triggerTemplate< zhongHan3DParams> _3D_2; + zhongHan2dCeLiangParams tongZhongHan2dCeLiangParams; + zhongHan2dCeLiangParams lvZhongHan2dCeLiangParams; + std::vector< zhongHan2dCeMianParams> tongZhongHan2dCeMianParams = std::vector< zhongHan2dCeMianParams>(2); + std::vector< zhongHan2dCeMianParams> lvZhongHan2dCeMianParams = std::vector< zhongHan2dCeMianParams>(2); + zhongHan3DParams tongZhongHan3DParams; + zhongHan3DParams lvZhongHan3DParams; + bool initOnce = false; + void onConfigReloaded() { + HDevEngineCpp::HDevEngine myEngine; + myEngine.UnloadAllProcedures(); + myEngine.SetProcedurePath("./halconAlgo/AlgoLibrary_604.hdpl"); + + if (initOnce == false) { + tongZhongHan2dCeLiangParams.seg_hanHouDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/hanYin/seg-hanHouDingWei-T.BHD", 1, 0, "铜Seg焊印定位网"); + lvZhongHan2dCeLiangParams.seg_hanHouDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/hanYin/seg-hanHouDingWei-L.BHD", 1, 1, "铝Seg焊印定位网"); + + tongZhongHan2dCeMianParams[0].seg_JiErDIngWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/pSeg_JiErDingWei_45_T_GPU0.BHD", 16, 0, "铜Seg极耳定位网"); + tongZhongHan2dCeMianParams[0].seg_ceMian = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_ZhongHan_HanHou_45_T_GPU0.BHD", 16, 0, "铜Seg侧面缺陷网"); + tongZhongHan2dCeMianParams[0].cls_ceMian = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_ZhongHan_HanHou_45_T_GPU0.BHD", 1, 0, "铜Cls侧面缺陷网"); + + tongZhongHan2dCeMianParams[1].seg_JiErDIngWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/pSeg_JiErDingWei_45_T_GPU0.BHD", 16, 0, "铝Seg极耳定位网"); + tongZhongHan2dCeMianParams[1].seg_ceMian = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_ZhongHan_HanHou_45_T_GPU0.BHD", 16, 0, "铝Seg侧面缺陷网"); + tongZhongHan2dCeMianParams[1].cls_ceMian = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_ZhongHan_HanHou_45_T_GPU0.BHD", 0, 1, "铝Cls侧面缺陷网"); + + lvZhongHan2dCeMianParams[0].seg_JiErDIngWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/pSeg_JiErDingWei_45_L_GPU1.BHD", 16, 1, ""); + lvZhongHan2dCeMianParams[0].seg_ceMian = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_ZhongHan_HanHou_45_L_GPU1.BHD", 16, 1, ""); + lvZhongHan2dCeMianParams[0].cls_ceMian = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_ZhongHan_HanHou_45_L_GPU1.BHD", 1, 1, ""); + + lvZhongHan2dCeMianParams[1].seg_JiErDIngWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/pSeg_JiErDingWei_45_L_GPU1.BHD", 16, 1, ""); + lvZhongHan2dCeMianParams[1].seg_ceMian = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_ZhongHan_HanHou_45_L_GPU1.BHD", 16, 1, ""); + lvZhongHan2dCeMianParams[1].cls_ceMian = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_ZhongHan_HanHou_45_L_GPU1.BHD", 1, 1, ""); + + lvZhongHan2dCeLiangParams.isTong = false; + + tongZhongHan3DParams.seg_3dHanYinDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_3dHanYin.BHD", 16, 0, ""); + lvZhongHan3DParams.seg_3dHanYinDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_3dHanYin.BHD", 16, 1, ""); + + initOnce = true; + } + } + bool initCustom() { +#pragma region 注册触发器回调 + lv_top_1.init("Camera1", "焊印检测-内-铜-相机1(90°)", { { 5360, 1,{5340,5420},"5362","5364",5800} }, 1, &tongZhongHan2dCeLiangParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); + lv_left45_2.init("Camera2", "翻折撕裂-内-铜-相机2(45°)", { { 5370, 1,{5340,5420},"5372","5374",-1} }, 1, &tongZhongHan2dCeMianParams[0], _serviceManager, bridge, zhongHan_hanHouCeMian); + lv_right45_3.init("Camera3", "翻折撕裂-内-铜-相机3(45°)", { { 5380,1,{5340,5420},"5382","5384",5406} }, 1, &tongZhongHan2dCeMianParams[1], _serviceManager, bridge, zhongHan_hanHouCeMian); + _3D_1.init("Camera4", "焊后检测-3D相机2(右)" , { { 5400, 1,{5340,5420},"5402","5404",-1} }, 1, &tongZhongHan3DParams, _serviceManager, bridge, zhongHan_hanHou3D); + + tong_top_5.init("Camera5", "焊印检测-外-铝-相机5(90°)", { { 5440, 1,{5340,5420},"5442","5444",5804} }, 1, &lvZhongHan2dCeLiangParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); + tong_left45_6.init("Camera6", "翻折撕裂-外-铝-相机6(45°)", { { 5450,1,{5340,5420},"5452","5454",-1} }, 1, &lvZhongHan2dCeMianParams[0], _serviceManager, bridge, zhongHan_hanHouCeMian); + tong_right45_7.init("Camera7", "翻折撕裂-外-铝-相机7(45°)", { { 5460, 1,{5340,5420},"5462","5464",5486} }, 1, &lvZhongHan2dCeMianParams[1], _serviceManager, bridge, zhongHan_hanHouCeMian); + _3D_2.init("Camera8", "焊后检测-3D相机1(左)", { { 5480, 1,{5340,5420},"5482","5484",-1} }, 1, &lvZhongHan3DParams, _serviceManager, bridge, zhongHan_hanHou3D); +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/581-BaoMo-Cam1213/CMakeLists.txt b/src/mission/601/601_ZhongHan_hanhou/CMakeLists.txt similarity index 100% rename from src/mission/581-BaoMo-Cam1213/CMakeLists.txt rename to src/mission/601/601_ZhongHan_hanhou/CMakeLists.txt diff --git a/src/mission/604/60项目代码设计规范.txt b/src/mission/604/60项目代码设计规范.txt new file mode 100644 index 0000000..a1227f0 --- /dev/null +++ b/src/mission/604/60项目代码设计规范.txt @@ -0,0 +1,4 @@ +halcon算法需使用halconGuard进行注册以及检测。 +handleAi模型需要handleAiGpuThreadGuard进行注册以及检测。 + +测量/数值类管控需要有上限,下限,判定启用,格式如[xxx上限(mm)][xxx判定启用]。可用位于paramSpecifications的genValueControlStruct函数生成,判断可使用valueJudgement。 \ No newline at end of file diff --git a/src/mission/604/CMakeLists - 副本.txt b/src/mission/604/CMakeLists - 副本.txt new file mode 100644 index 0000000..f9a95fd --- /dev/null +++ b/src/mission/604/CMakeLists - 副本.txt @@ -0,0 +1,11 @@ +# CMakeList.txt: 遍历文件夹 添加子项目 + +set(OUR_LIBS "") +SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF) +message("OUR_LIBS :" ${OUR_LIBS}) + + +# 遍历子库 +foreach(dll ${OUR_LIBS}) + add_subdirectory(${dll}) +endforeach(dll ${}) diff --git a/src/mission/604/CMakeLists.txt b/src/mission/604/CMakeLists.txt new file mode 100644 index 0000000..f9a95fd --- /dev/null +++ b/src/mission/604/CMakeLists.txt @@ -0,0 +1,11 @@ +# CMakeList.txt: 遍历文件夹 添加子项目 + +set(OUR_LIBS "") +SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF) +message("OUR_LIBS :" ${OUR_LIBS}) + + +# 遍历子库 +foreach(dll ${OUR_LIBS}) + add_subdirectory(${dll}) +endforeach(dll ${}) diff --git a/src/mission/604/DGH/604-Dgh-CB-1/604-Dgh-CB-1.cpp b/src/mission/604/DGH/604-Dgh-CB-1/604-Dgh-CB-1.cpp new file mode 100644 index 0000000..6de7e5a --- /dev/null +++ b/src/mission/604/DGH/604-Dgh-CB-1/604-Dgh-CB-1.cpp @@ -0,0 +1,8 @@ +#include <604-Dgh-CB-1.h> + +mission* createMission() { + return new _604_Dgh(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/DGH/604-Dgh-CB-1/604-Dgh-CB-1.h b/src/mission/604/DGH/604-Dgh-CB-1/604-Dgh-CB-1.h new file mode 100644 index 0000000..e0d2141 --- /dev/null +++ b/src/mission/604/DGH/604-Dgh-CB-1/604-Dgh-CB-1.h @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include +#include +#pragma region 算法 + +using namespace HDevEngineCpp; + +#pragma endregion + +struct dghGapDetectInput { + HObject in_imageH; + HObject in_imageLum; + HTuple in_MeasureNumber; + HTuple in_xResolution; + HTuple in_yResolution; + HTuple in_zResolution; + HTuple in_Location; +}; + +struct dghGapDetectOutput { + HObject out_DisplayImage; + HTuple out_MaxHeight; + HTuple out_MaxWidth; + HTuple out_ResultRow; + HTuple out_ResultColumn; + HTuple out_Flag; + HTuple out_NgTails; + HTuple out_DisplayRows; + HTuple out_DisplayCols; +}; +class dghYuHanQianParam { +public: + std::shared_ptr> evaluater + = std::make_shared>(); + dghYuHanQianParam() { + evaluater->init("_6_detect_gap_width_height", + [](std::map& inputHObj, + std::map& inputHTuple, + const dghGapDetectInput& in) { + inputHObj["in_imageH"] = in.in_imageH; + inputHObj["in_imageLum"] = in.in_imageLum; + inputHTuple["in_MeasureNumber"] = in.in_MeasureNumber; + inputHTuple["in_xResolution"] = in.in_xResolution; + inputHTuple["in_yResolution"] = in.in_yResolution; + inputHTuple["in_zResolution"] = in.in_zResolution; + inputHTuple["in_Location"] = in.in_Location; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + dghGapDetectOutput& out) { + out.out_DisplayImage = outputHObj["out_DisplayImage"]; + out.out_MaxHeight = outputHTuple["out_MaxHeight"]; + out.out_MaxWidth = outputHTuple["out_MaxWidth"]; + out.out_ResultRow = outputHTuple["out_ResultRow"]; + out.out_ResultColumn = outputHTuple["out_ResultColumn"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgTails = outputHTuple["out_NgTails"]; + out.out_DisplayRows = outputHTuple["out_DisplayRows"]; + out.out_DisplayCols = outputHTuple["out_DisplayCols"]; + }); + } +}; + +void exceptionResume(std::shared_ptr data, yh_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob(why.toStdString(), cv::Point(50, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,30f"); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈//原来是异常发送NG-2,改成异常发送3,表示取图失败 + else if (why == "取图超时") + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_3");//结果反馈//原来是异常发送NG-2,改成异常发送3,表示取图失败 + else + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈//原来是异常发送NG-2,改成异常发送3,表示取图失败 +} +void drawSlideTip(cv::Mat& Paint, const int& triggerAddr) {//start 2200 0 + cvPainter textPainter(50); + drawArrow(Paint, cv::Point(2100, 60), 100, true, cv::Scalar(0, 255, 0), 3, 10); + textPainter.appendPaintJob("来料方向", cv::Point(2100, 0), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + std::vector> lineDrawYPoints; + for (int i = 0; i < 4; i++) { + cv::rectangle(Paint, cv::Rect(2300 + i * 200, 25, 150, 50), cv::Scalar(0, 255, 0), 5);//画电池对应矩形 + lineDrawYPoints.push_back(std::make_pair(2300 + i * 200, 2300 + i * 200 + 150)); + } + switch (triggerAddr) {//画扫描边 + case 18000://相机3 扫描顺序电池1 来料顺序电池4 外侧 + cv::line(Paint, cv::Point(lineDrawYPoints[3].first, 25), cv::Point(lineDrawYPoints[3].second, 25), cv::Scalar(0, 0, 255), 5); + break; + case 18002://相机3 扫描顺序电池2 来料顺序电池3 外侧 + cv::line(Paint, cv::Point(lineDrawYPoints[2].first, 25), cv::Point(lineDrawYPoints[2].second, 25), cv::Scalar(0, 0, 255), 5); + break; + case 18004://相机3 扫描顺序电池3 来料顺序电池2 外侧 + cv::line(Paint, cv::Point(lineDrawYPoints[1].first, 25), cv::Point(lineDrawYPoints[1].second, 25), cv::Scalar(0, 0, 255), 5); + break; + case 18006://相机3 扫描顺序电池4 来料顺序电池1 外侧 + cv::line(Paint, cv::Point(lineDrawYPoints[0].first, 25), cv::Point(lineDrawYPoints[0].second, 25), cv::Scalar(0, 0, 255), 5); + break; + + case 18200://相机4 扫描顺序电池1 来料顺序电池4 内侧 + cv::line(Paint, cv::Point(lineDrawYPoints[3].first, 75), cv::Point(lineDrawYPoints[3].second, 75), cv::Scalar(0, 0, 255), 5); + break; + case 18202://相机4 扫描顺序电池2 来料顺序电池3 内侧 + cv::line(Paint, cv::Point(lineDrawYPoints[2].first, 75), cv::Point(lineDrawYPoints[2].second, 75), cv::Scalar(0, 0, 255), 5); + break; + case 18204://相机4 扫描顺序电池3 来料顺序电池2 内侧 + cv::line(Paint, cv::Point(lineDrawYPoints[1].first, 75), cv::Point(lineDrawYPoints[1].second, 75), cv::Scalar(0, 0, 255), 5); + break; + case 18206://相机4 扫描顺序电池4 来料顺序电池1 内侧 + cv::line(Paint, cv::Point(lineDrawYPoints[0].first, 75), cv::Point(lineDrawYPoints[0].second, 75), cv::Scalar(0, 0, 255), 5); + break; + + case 18400://相机2 扫描顺序电池1 来料顺序电池4 外侧 + cv::line(Paint, cv::Point(lineDrawYPoints[3].first, 25), cv::Point(lineDrawYPoints[3].second, 25), cv::Scalar(0, 0, 255), 5); + break; + case 18402://相机2 扫描顺序电池2 来料顺序电池3 外侧 + cv::line(Paint, cv::Point(lineDrawYPoints[2].first, 25), cv::Point(lineDrawYPoints[2].second, 25), cv::Scalar(0, 0, 255), 5); + break; + case 18404://相机2 扫描顺序电池3 来料顺序电池2 外侧 + cv::line(Paint, cv::Point(lineDrawYPoints[1].first, 25), cv::Point(lineDrawYPoints[1].second, 25), cv::Scalar(0, 0, 255), 5); + break; + case 18406://相机2 扫描顺序电池4 来料顺序电池1 外侧 + cv::line(Paint, cv::Point(lineDrawYPoints[0].first, 25), cv::Point(lineDrawYPoints[0].second, 25), cv::Scalar(0, 0, 255), 5); + break; + + case 18600://相机1 扫描顺序电池1 来料顺序电池4 内侧 + cv::line(Paint, cv::Point(lineDrawYPoints[3].first, 75), cv::Point(lineDrawYPoints[3].second, 75), cv::Scalar(0, 0, 255), 5); + break; + case 18602://相机1 扫描顺序电池2 来料顺序电池3 内侧 + cv::line(Paint, cv::Point(lineDrawYPoints[2].first, 75), cv::Point(lineDrawYPoints[2].second, 75), cv::Scalar(0, 0, 255), 5); + break; + case 18604://相机1 扫描顺序电池3 来料顺序电池2 内侧 + cv::line(Paint, cv::Point(lineDrawYPoints[1].first, 75), cv::Point(lineDrawYPoints[1].second, 75), cv::Scalar(0, 0, 255), 5); + break; + case 18606://相机1 扫描顺序电池4 来料顺序电池1 内侧 + cv::line(Paint, cv::Point(lineDrawYPoints[0].first, 75), cv::Point(lineDrawYPoints[0].second, 75), cv::Scalar(0, 0, 255), 5); + break; + } + +} +static bool yuHanQian3D(std::shared_ptr data, dghYuHanQianParam* _detParams, yh_581_address address) { + try { + std::vector defects; + QDateTime createTime = QDateTime::currentDateTime(); + double minusHeightThreshold = data->service->_paramSyncManager->get("预焊前检测", "高度/台阶差负方向最大阈值", 0.3).toDouble(); + double positiveHeightThreshold = data->service->_paramSyncManager->get("预焊前检测", "高度/台阶差正方向最大阈值", 0.0).toDouble(); + double widthThreshold = data->service->_paramSyncManager->get("预焊前检测", "缝隙值最大阈值", 0.1).toDouble(); + double MeasureNumber = data->service->_paramSyncManager->get("预焊前检测", "焊点的测量数量", 4).toDouble(); + + if (data->getOriImgSize() < 2) { + exceptionResume(data, address, "NG"); + return true; + } + if (data->getOriImg(0).cols == 1000) { + exceptionResume(data, address, "取图超时"); + return true; + } + cv::Mat heightImg = data->getOriImg(1); + cv::Mat lumImg = data->getOriImg(0); + cv::Mat Paint; + + dghGapDetectInput hInput; + hInput.in_imageH = MatToHObject(heightImg); + hInput.in_imageLum = MatToHObject(lumImg); + hInput.in_MeasureNumber = MeasureNumber; + hInput.in_xResolution = 0.0025; + hInput.in_yResolution = data->service->_paramSyncManager->get("预焊前检测", "3D相机Y方向精度",0.177).toFloat(); + hInput.in_zResolution = 0.0004; + hInput.in_Location = "Right"; + auto hOutput = _detParams->evaluater->evaluate(hInput); + if (!hOutput.runFlag) { + throw std::runtime_error(hOutput.faildResult); + } + auto& halconOutput = hOutput.result; + int flag = halconOutput.out_Flag.D(); + cvPainter textPainter(75); + + textPainter.appendPaintJob(data->SNcode, cv::Point(1000,0), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob(data->cameraKeyName, cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + if (address.triggerAddr == 18000 || address.triggerAddr == 18200 || address.triggerAddr == 18400 || address.triggerAddr == 18600) { + textPainter.appendPaintJob("扫描顺序:1", cv::Point(1300, 50), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("来料顺序:4", cv::Point(1600, 50), cv::Scalar(0, 255, 0)); + } + else if (address.triggerAddr == 18002 || address.triggerAddr == 18202 || address.triggerAddr == 18402 || address.triggerAddr == 18602) { + textPainter.appendPaintJob("扫描顺序:2", cv::Point(1300, 50), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("来料顺序:3", cv::Point(1600, 50), cv::Scalar(0, 255, 0)); + } + else if (address.triggerAddr == 18004 || address.triggerAddr == 18204 || address.triggerAddr == 18404 || address.triggerAddr == 18604) { + textPainter.appendPaintJob("扫描顺序:3", cv::Point(1300, 50), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("来料顺序:2", cv::Point(1600, 50), cv::Scalar(0, 255, 0)); + } + else if (address.triggerAddr == 18006 || address.triggerAddr == 18206 || address.triggerAddr == 18406 || address.triggerAddr == 18606) { + textPainter.appendPaintJob("扫描顺序:4", cv::Point(1300, 50), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("来料顺序:1", cv::Point(1600, 50), cv::Scalar(0, 255, 0)); + } + bool isOK = true; + bool WidthOpen = data->service->_paramSyncManager->get("预焊前检测", "缝隙值检测开启", true).toBool(); + bool HeightOpen = data->service->_paramSyncManager->get("预焊前检测", "台阶差检测开启", true).toBool(); + QByteArray gaoDuChaSendText, fengXiSendText; + QByteArray extraDataSend; + if (flag == 1) { + int yStart = -25; + for (int i = 0; i < halconOutput.out_DisplayRows.Length(); i++) { + double MaxHeight = halconOutput.out_MaxHeight[i].D(); + double MaxWidth = halconOutput.out_MaxWidth[i].D(); + + cv::Point displayPos( 1000, halconOutput.out_DisplayRows[i].D() - 50); + textPainter.appendPaintJob("检测区域" + std::to_string(i + 1) + ":", displayPos, cv::Scalar(0, 255, 0)); + + displayPos.x += 280; + if ((MaxHeight > 0) && std::abs(MaxHeight) > std::abs(positiveHeightThreshold) || //正方向 + (MaxHeight < 0) && std::abs(MaxHeight) > std::abs(minusHeightThreshold)) {//负方向 + textPainter.appendPaintJob("高度/台阶差" + QString::number(MaxHeight, 'f', 3).toStdString() +"mm", displayPos, cv::Scalar(0, 0, 255)); + if(HeightOpen) isOK = false; + } + else + textPainter.appendPaintJob("高度/台阶差" + QString::number(MaxHeight, 'f', 3).toStdString() + "mm", displayPos, cv::Scalar(0, 255, 0)); + + displayPos.x += 600; + if (std::abs(MaxWidth) > std::abs(widthThreshold)) { + textPainter.appendPaintJob("缝隙值" + QString::number(MaxWidth, 'f', 3).toStdString() + "mm", displayPos, cv::Scalar(0, 0, 255)); + if(WidthOpen) isOK = false; + } + else + textPainter.appendPaintJob("缝隙值" + QString::number(MaxWidth, 'f', 3).toStdString() + "mm", displayPos, cv::Scalar(0, 255, 0)); + if (i == 3) { + extraDataSend += QByteArray::number(MaxWidth) + "," + QByteArray::number(MaxHeight); + } + else { + gaoDuChaSendText += QByteArray::number(MaxHeight) + ","; + fengXiSendText += QByteArray::number(MaxWidth) + ","; + } + } + } + else { + gaoDuChaSendText = "-1,-1,-1,"; + fengXiSendText = "-1,-1,-1,"; + std::string ngDetailStr = gbkToUtf8(halconOutput.out_NgTails.ToString().Text()); + ngDetailStr = ngDetailStr.substr(1, ngDetailStr.length() - 2); + textPainter.appendPaintJob(ngDetailStr, cv::Point(50, 450), cv::Scalar(0, 0, 255)); + for (int i = 0; i < halconOutput.out_ResultRow.Length(); i++) { + textPainter.appendPaintJob(cv::Point(halconOutput.out_ResultColumn[i].D(), halconOutput.out_ResultRow[i].D()),1, cv::Scalar(0, 0, 255), 1,0); + } + data->halconOK = false; + isOK = false; + } + QByteArray plcResult = "1"; + if (isOK == false) { + textPainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + plcResult = "2"; + data->halconOK = false; + defects.push_back("台阶/间隙不良"); + } + else + textPainter.appendPaintJob("OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + + //发送参数1 + data->service->_triggerManager->writeFloat("D" + + QByteArray::number(address.valueOffsetAddr) + "_" + + fengXiSendText + "1,1," + + gaoDuChaSendText + plcResult + "," + plcResult + "," + plcResult + "," + plcResult +"," + plcResult + "," + plcResult + "," + extraDataSend);//数值1 + + //发送结果 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + Paint = HObject2Mat(halconOutput.out_DisplayImage); + textPainter.executePaintJobs(Paint); + int rate = MIN(Paint.rows, Paint.cols) / 20; + cvPainter textPainter1(rate); + textPainter1.appendPaintJob("当前条码:" + data->SNcode, cv::Point(50, Paint.rows - (rate * 2)), cv::Scalar(0, 255, 255)); + textPainter1.appendPaintJob("获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - rate), cv::Scalar(0, 255, 255)); + textPainter1.executePaintJobs(Paint); + drawSlideTip(Paint, address.triggerAddr); + data->insertResultImg(Paint); + return true; + } + CATCH_EXCEPTIONS + exceptionResume(data, address, "NG"); + return true; +} +class _604_Dgh : public mission { +public: + + _581_Single_triggerTemplate dgh_cb_yh_3d_1; + _581_Single_triggerTemplate dgh_cb_yh_3d_2; + _581_Single_triggerTemplate dgh_cb_yh_3d_3; + _581_Single_triggerTemplate dgh_cb_yh_3d_4; + + std::vector _params = std::vector(4); + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + } + bool initCustom() { +#pragma region 注册触发器回调 + dgh_cb_yh_3d_1.init("Camera1", "长边预焊前3D-1-外左", 18000, 4, 1, &_params[0], _serviceManager, bridge, yuHanQian3D); + dgh_cb_yh_3d_1.address[1].valueOffsetAddr += 20; + dgh_cb_yh_3d_1.address[2].valueOffsetAddr = 18914;//借用满焊1的地址 + dgh_cb_yh_3d_1.address[3].valueOffsetAddr = 18954;//借用满焊1的地址 + dgh_cb_yh_3d_2.init("Camera2", "长边预焊前3D-2-内左", 18200, 4, 1, &_params[1], _serviceManager, bridge, yuHanQian3D); + dgh_cb_yh_3d_2.address[1].valueOffsetAddr += 20; + dgh_cb_yh_3d_2.address[2].valueOffsetAddr = 19114;//借用满焊2的地址 + dgh_cb_yh_3d_2.address[3].valueOffsetAddr = 19154;//借用满焊2的地址 + dgh_cb_yh_3d_3.init("Camera3", "长边预焊前3D-3-外右", 18400, 4, 1, &_params[2], _serviceManager, bridge, yuHanQian3D); + dgh_cb_yh_3d_3.address[1].valueOffsetAddr += 20; + dgh_cb_yh_3d_3.address[2].valueOffsetAddr = 19314;//借用满焊3的地址 + dgh_cb_yh_3d_3.address[3].valueOffsetAddr = 19354;//借用满焊3的地址 + dgh_cb_yh_3d_4.init("Camera4", "长边预焊前3D-4-内右", 18600, 4, 1, &_params[3], _serviceManager, bridge, yuHanQian3D); + dgh_cb_yh_3d_4.address[1].valueOffsetAddr += 20; + dgh_cb_yh_3d_4.address[2].valueOffsetAddr = 19514;//借用满焊4的地址 + dgh_cb_yh_3d_4.address[3].valueOffsetAddr = 19554;//借用满焊4的地址 +#pragma endregion + return true; + } + +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/581-BaoMo-Cam91011/CMakeLists.txt b/src/mission/604/DGH/604-Dgh-CB-1/CMakeLists.txt similarity index 100% rename from src/mission/581-BaoMo-Cam91011/CMakeLists.txt rename to src/mission/604/DGH/604-Dgh-CB-1/CMakeLists.txt diff --git a/src/mission/604/DGH/CMakeLists.txt b/src/mission/604/DGH/CMakeLists.txt new file mode 100644 index 0000000..f9a95fd --- /dev/null +++ b/src/mission/604/DGH/CMakeLists.txt @@ -0,0 +1,11 @@ +# CMakeList.txt: 遍历文件夹 添加子项目 + +set(OUR_LIBS "") +SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF) +message("OUR_LIBS :" ${OUR_LIBS}) + + +# 遍历子库 +foreach(dll ${OUR_LIBS}) + add_subdirectory(${dll}) +endforeach(dll ${}) diff --git a/src/mission/604/RLJ/604_RuanLianJie_damianjiance/604_RuanLianJie_damianjiance.cpp b/src/mission/604/RLJ/604_RuanLianJie_damianjiance/604_RuanLianJie_damianjiance.cpp new file mode 100644 index 0000000..05bc1b9 --- /dev/null +++ b/src/mission/604/RLJ/604_RuanLianJie_damianjiance/604_RuanLianJie_damianjiance.cpp @@ -0,0 +1,9 @@ +#include <604_RuanLianJie_damianjiance.h> + +mission* createMission() { + return new _RuanLianJie_damianjiance(); +} + +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/RLJ/604_RuanLianJie_damianjiance/604_RuanLianJie_damianjiance.h b/src/mission/604/RLJ/604_RuanLianJie_damianjiance/604_RuanLianJie_damianjiance.h new file mode 100644 index 0000000..8034a2e --- /dev/null +++ b/src/mission/604/RLJ/604_RuanLianJie_damianjiance/604_RuanLianJie_damianjiance.h @@ -0,0 +1,663 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; + +#pragma endregion +class RuanLianJie_damianjiance { +public: + std::shared_ptr dingwei_seg = nullptr; + std::shared_ptr seg = nullptr; + std::shared_ptr cls = nullptr; + cv::Mat TP1IMG, TP2Paint; +}; +QByteArray keMaResult = "0"; +QByteArray okNgSaveText = "OK"; + +cv::Mat cutLabelImg(cv::Mat pic, int W, int H, cv::Rect rect, int edgeType, int largeType, int color) +{ + cv::Point center; + center.x = rect.x + rect.width / 2; + center.y = rect.y + rect.height / 2; + + if (rect.width <= W && rect.height <= H) + { + if (edgeType == 0) + { + cv::Rect rec = { center.x - W / 2,center.y - H / 2,W,H }; + if (rec.x < 0) + rec.x = 0; + if (rec.y < 0) + rec.y = 0; + if (rec.x + rec.width > pic.cols) + rec.x = pic.cols - rec.width; + if (rec.y + rec.height > pic.rows) + rec.y = pic.rows - rec.height; + + cv::Mat picCut; + pic(rec).copyTo(picCut); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + + int x, y; + cv::Rect rec; + x = center.x - W / 2; + y = center.y - H / 2; + rec.x = x; + rec.y = y; + rec.width = W; + rec.height = H; + cv::Mat picCut(H, W, pic.type(), cv::Scalar(color, color, color)); + if (x < 0) + { + rec.width += rec.x; + xDiff = 0 - rec.x; + rec.x = 0; + } + if (y < 0) + { + rec.height += rec.y; + yDiff = 0 - rec.y; + rec.y = 0; + } + if (rec.x + rec.width > pic.cols) + rec.width = pic.cols - rec.x; + if (rec.y + rec.height > pic.rows) + rec.height = pic.rows - rec.y; + + pic(rec).copyTo(picCut({ xDiff, yDiff, rec.width, rec.height })); + return picCut; + } + } + else + { + if (largeType == 0) + { + int xDiff = 0; + int yDiff = 0; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rect.x < 0) + { + rect.width += rect.x; + rect.x = 0; + } + if (rect.y < 0) + { + rect.height += rect.y; + rect.y = 0; + } + if (rect.x + rect.width > pic.cols) + rect.width = pic.cols - rect.x; + if (rect.y + rect.height > pic.rows) + rect.height = pic.rows - rect.y; + + xDiff = (wTem - rect.width) * 1.0 / 2; + yDiff = (hTem - rect.height) * 1.0 / 2; + + pic(rect).copyTo(picCut({ xDiff, yDiff, rect.width, rect.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + cv::Rect rectTem; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + rectTem = { rect.x + rect.width / 2 - wTem / 2,rect.y + rect.height / 2 - hTem / 2,wTem,hTem }; + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rectTem.x < 0) + { + rectTem.width += rectTem.x; + xDiff = 0 - rectTem.x; + rectTem.x = 0; + } + if (rectTem.y < 0) + { + rectTem.height += rectTem.y; + yDiff = 0 - rectTem.y; + rectTem.y = 0; + } + if (rectTem.x + rectTem.width > pic.cols) + rectTem.width = pic.cols - rectTem.x; + if (rectTem.y + rectTem.height > pic.rows) + rectTem.height = pic.rows - rectTem.y; + + pic(rectTem).copyTo(picCut({ xDiff, yDiff, rectTem.width, rectTem.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + } + + return cv::Mat(); +} + +cv::Rect getImageLocateRect(cv::Mat& src, handdleAI::pSegmentation* seg, double bounus = 0.1) { + cv::Mat tmpImg; + cv::resize(src, tmpImg, cv::Size(), bounus, bounus); + cv::Mat ouput = ~seg->Evaluate(tmpImg); + cv::Rect rect = findMaxAreaContour(ouput); + rect.x /= bounus; rect.y /= bounus; + rect.width /= bounus; rect.height /= bounus; + return rect; +} +void exceptionResume(std::shared_ptr data, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_1");//结果反馈 +} + +//bool damianjiance(std::shared_ptr data, RuanLianJie_damianjiance* _detParams, sx_581_address address) { +// try { +//#if 0 +// +//#else +// +// QByteArray plcResult = "1"; +// cv::Mat imgResize1, imgResize2; +// if (data->oriImgs.size() < 1) { +// exceptionResume(data, _detParams, address, "图片数量不满足"); +// return true; +// } +// if (data->getOriImg(0).rows == 200) { +// exceptionResume(data, _detParams, address, "取图超时"); +// return true; +// } +// cv::Mat Paint; +// if (data->getOriImg(0).channels() == 1) +// cvtColor(data->getOriImg(0), Paint, cv::COLOR_GRAY2BGR); +// else +// data->getOriImg(0).copyTo(Paint); +// cv::Mat src = data->getOriImg(0); +// +// //////////////////////检测////////////////////// +// cv::Mat afterDingWei; +// { +// cv::Mat forDingWei; +// cv::resize(src, forDingWei, cv::Size(), 0.1, 0.1); +// cv::Mat segResult = ~_detParams->dingwei_seg->Evaluate(forDingWei); +// auto rect = findMaxAreaContour(segResult); +// rect.x *= 10; rect.y *= 10; rect.width *= 10; rect.height *= 10; +// if (rect.width < 4000 || rect.height < 4000) { +// qDebug() << "定位失败"; +// return true; +// } +// src(rect).copyTo(afterDingWei); +// } +// cv::resize(afterDingWei, afterDingWei, cv::Size(), 0.5, 0.5); +// if (afterDingWei.channels() == 1) +// cv::cvtColor(afterDingWei, Paint, cv::COLOR_GRAY2RGB); +// else +// afterDingWei.copyTo(Paint); +// +// cv::Mat segResult = ~_detParams->seg->Evaluate(afterDingWei); +// //data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1"); +// std::vector>contours; +// cv::findContours(segResult, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); +// bool isNg = false; +// TextRendererZH textPainter(Paint.cols, Paint.rows, 500); +// for (int i = 0; i < contours.size(); i++) { +// cv::Rect rect = cv::boundingRect(contours[i]); +// if (rect.width < 60 || rect.height < 60) +// continue; +// std::vector clsImgs = cutImg2ClsVec(rect, afterDingWei); +// bool clsIsNg = false; +// for (auto& clsImg : clsImgs) { +// int clsResult = _detParams->cls->Evaluate(clsImg); +// if (clsResult == 2) +// clsIsNg = true; +// } +// if (clsIsNg == true) +// textPainter.appendPaintJob(contours[i], cv::Scalar(0, 0, 255), 3); +// +// for (auto& clsImg : clsImgs) +// data->clsOriginImgs.push_back({ clsIsNg == true ? "NG" : "OK",clsImg }); +// } +// if (isNg == true) +// textPainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); +// else +// textPainter.appendPaintJob("OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); +// if (data->service->_paramSyncManager->get("通用设置","强制OK", false).toBool()) +// plcResult = "1"; +// data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 +// data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_1");//结果反馈 +// +// textPainter.excusePaintJobs(Paint); +// data->segOriginImgs.push_back(afterDingWei); +// data->resultImgs.push_back(Paint); +// cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); +// data->resultImgs.push_back(Paint); +// return true; +//#endif +// } +// CATCH_EXCEPTIONS +// +// qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); +// exceptionResume(data, _detParams, address, "检测失败"); +// return true; +//} + +bool damianjiance(std::shared_ptr data, RuanLianJie_damianjiance* _detParams, sx_581_address address) { + try { + float minWidth = data->service->_paramSyncManager->get("大面检测", "Seg过滤宽(pix)", 60).toFloat(); + float minHeight = data->service->_paramSyncManager->get("大面检测", "Seg过滤高(pix)", 60).toFloat(); + float clsProbThreshold = data->service->_paramSyncManager->get("大面检测", "cls过滤分数", 0.998).toFloat(); + bool detectEnable = data->service->_paramSyncManager->get("大面检测", "薄膜破损检测开启", true).toBool(); + //float minArea = data->service->_paramSyncManager->get("大面检测", "Seg过滤面积(pix)", 500).toFloat(); + QByteArray plcResult = "1"; + cv::Mat imgResize1, imgResize2; + std::vectordefects; + /*if (data->oriImgs.size() < 1) { + exceptionResume(data, _detParams, address, "图片数量不满足"); + return true; + } + if (data->getOriImg(0).rows == 200) { + exceptionResume(data, _detParams, address, "取图超时"); + return true; + }*/ + cv::Mat Paint; + if (data->getOriImg(0).channels() == 1) + cvtColor(data->getOriImg(0), Paint, cv::COLOR_GRAY2BGR); + else + data->getOriImg(0).copyTo(Paint); + cv::Mat src = data->getOriImg(0); + + src = cv::imread("E:\\test\\1.bmp", -1); + + //////////////////////检测////////////////////// + float resizeRate = 10; + cv::Mat resizeImage; + cv::resize(src, resizeImage, cv::Size(), 1 / resizeRate, 1 / resizeRate, cv::INTER_AREA); + cv::Mat binaryImage1 = ~_detParams->dingwei_seg->Evaluate(resizeImage);//seg模型调用,获取产品位置的二值图轮廓 + cv::Rect cutRect = findMaxAreaContour(binaryImage1);//找最大的轮廓,注意:输入的二值图为黑色背景,白色缺陷轮廓(黑色中找白色) + //把找到的轮廓位置还原到原图 + { + cutRect.x = cutRect.x * resizeRate - 75; + cutRect.y = cutRect.y * resizeRate - 75; + cutRect.width = cutRect.width * resizeRate + 150; + cutRect.height = cutRect.height * resizeRate + 150; + } + + if (cutRect.width < 4000 || cutRect.height < 4000) { + qDebug() << "定位失败"; + return true; + } + + cutRect = cutRect & cv::Rect(0, 0, src.cols, src.rows);//防止切图越界 + + cv::Mat afterDingWei; + src(cutRect).copyTo(afterDingWei); + + float resizeRateX = 2800.0 / afterDingWei.cols; + float resizeRateY = 2600.0 / afterDingWei.rows; + cv::resize(afterDingWei, afterDingWei, cv::Size(), resizeRateX, resizeRateY, cv::INTER_AREA); + + //cv::Mat Paint; + if (afterDingWei.channels() == 1) + cv::cvtColor(afterDingWei, Paint, cv::COLOR_GRAY2RGB); + else + afterDingWei.copyTo(Paint); + cv::Mat segResult = ~_detParams->seg->Evaluate(afterDingWei); + + std::vector>contours; + cv::findContours(segResult, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + bool isNg = false; + cvPainter textPainter(300); + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + + cv::Rect RealRect; + RealRect.x = rect.x / resizeRateX + cutRect.x; + RealRect.y = rect.y / resizeRateY + cutRect.y; + RealRect.width = rect.width / resizeRateX; + RealRect.height = rect.height / resizeRateY; + + if (RealRect.width < minWidth || RealRect.height < minHeight) + continue; + std::vector clsImgs; + clsImgs.push_back(cutLabelImg(afterDingWei, 128, 128, rect, 1, 1, 0)); + bool clsIsNg = false; + int clsResult = 0; + for (auto& clsImg : clsImgs) { + auto clsResult = _detParams->cls->Evaluate(clsImg); + float prob = clsResult.prob; + if (clsResult.classes == 2) + clsIsNg = true; + + switch (clsResult.classes) { + case 0: { + //"OK" + break; + } + case 1: { + //"破损" + if (prob > clsProbThreshold && detectEnable == true) { + + clsIsNg = true; + isNg = true; + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + } + break; + } + case 2: { + //"脏污" + break; + } + case 3: { + //"褶皱" + break; + } + case 4: { + //"贴胶" + break; + } + case 5: { + //"其他" + break; + } + default: { + //"未知缺陷" + break; + } + } + } + if (clsIsNg == true) { + //textPainter.appendPaintJob(contours[i], cv::Scalar(0, 0, 255), 3); + } + + for (auto& clsImg : clsImgs) + data->insertClsOriginImg({ std::make_pair(std::to_string(clsResult),clsImg) }); + } + if (isNg == true) + textPainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + else + textPainter.appendPaintJob("OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_1");//结果反馈 + + textPainter.executePaintJobs(Paint); + data->insertSegOriginImg(afterDingWei); + data->insertResultImg(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + exceptionResume(data, address, "检测失败"); + + return true; +} +bool BaoMoQiangDaMian_Test(std::shared_ptr data, RuanLianJie_damianjiance* _detParams, sx_581_address address) { + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + ///////////////////取图//处理////////////////// + try { + QDateTime createTime = QDateTime::currentDateTime(); + QByteArray plcResult = "1"; + QString objectName = "大面检测"; + std::vectordefects; + double resizeX = data->service->_paramSyncManager->get(objectName, "X方向缩放倍率", 0.43).toDouble(); + double resizeY = data->service->_paramSyncManager->get(objectName, "Y方向缩放倍率", 0.405).toDouble(); + int extraSize = data->service->_paramSyncManager->get(objectName, "定位后外扩大小(pix)", 75).toInt(); + double in_xResolution = data->service->_paramSyncManager->get(objectName, "相机x方向精度", 1.0).toDouble(); + double in_yResolution = data->service->_paramSyncManager->get(objectName, "相机y方向精度", 1.0).toDouble(); + double MinWidth = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值宽(mm)", 60.0).toDouble(); + double MinHeight = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值高(mm)", 60.0).toDouble(); + double ignoreWidth = data->service->_paramSyncManager->get(objectName, "Seg不检测阈值宽(mm)", 0.5).toDouble(); + double ignoreHeight = data->service->_paramSyncManager->get(objectName, "Seg不检测阈值高(mm)", 0.5).toDouble(); + double MinArea = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值面积(mm²)", 60.0).toDouble(); + double clsProbThreshold = data->service->_paramSyncManager->get(objectName, "Cls置信度阈值下限", 0.9).toDouble(); + int textOffset = data->service->_paramSyncManager->get(objectName, "缺陷打印位置Y方向偏移", 10).toInt(); + bool saveLocateTrainImg = data->service->_paramSyncManager->get(objectName, "保存定位网训练用图片", true).toBool(); + cv::Mat src = data->getOriImg(0); + //src = cv::imread("E:\\test\\1.bmp", -1); + //src = cv::imread("D:\\readSnEmpty09-53-06-55_Camera1_ORI_0_09-53-07-892.bmp"); + //////////////////////检测////////////////////// + + float resizeRate = 10; + cv::Mat resizeImage; + cv::resize(src, resizeImage, cv::Size(), 1 / resizeRate, 1 / resizeRate, cv::INTER_AREA); + cv::Mat binaryImage1 = ~_detParams->dingwei_seg->Evaluate(resizeImage);//seg模型调用,获取产品位置的二值图轮廓 + cv::Rect cutRect = findMaxAreaContour(binaryImage1);//找最大的轮廓,注意:输入的二值图为黑色背景,白色缺陷轮廓(黑色中找白色) + //把找到的轮廓位置还原到原图 + { + cutRect.x = cutRect.x * resizeRate - 75; + cutRect.y = cutRect.y * resizeRate - 75; + cutRect.width = cutRect.width * resizeRate + 150; + cutRect.height = cutRect.height * resizeRate + 150; + } + + cutRect = cutRect & cv::Rect(0, 0, src.cols, src.rows);//防止切图越界 + + cv::Mat afterDingWei; + src(cutRect).copyTo(afterDingWei); + + float resizeRateX = 2800.0 / afterDingWei.cols; + float resizeRateY = 2600.0 / afterDingWei.rows; + cv::resize(afterDingWei, afterDingWei, cv::Size(), resizeRateX, resizeRateY, cv::INTER_AREA); + + cv::Mat Paint; + if (afterDingWei.channels() == 1) + cv::cvtColor(afterDingWei, Paint, cv::COLOR_GRAY2RGB); + else + afterDingWei.copyTo(Paint); + + data->insertSegOriginImg(afterDingWei); + //data->insertSegOriginImg(afterDingWei); + cv::Mat segResult = ~_detParams->seg->Evaluate(afterDingWei); + data->insertSegOriginImg(segResult); + //data->insertSegOriginImg(segResult); + //data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1"); + std::vector>contours; + cv::findContours(segResult, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + bool isNg = false; + TextRendererZH textPainter(Paint.cols, Paint.rows, 500); + + std::vector> clsImgs; + TextRendererZH textPainter1(Paint.cols, Paint.rows, 30); + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + float realArea = cv::contourArea(contours[i]) * in_yResolution * in_xResolution / resizeRateX / resizeRateY; + float realHeight = rect.height * in_yResolution / resizeRateY; + float realWidth = rect.width * in_xResolution / resizeRateX; + QString sizeStr = "宽:" + QString::number(realWidth, 'f', 3) + "mm高:" + + QString::number(realHeight, 'f', 3) + "mm面积:" + QString::number(realArea, 'f', 3) + "mm²"; + if (realWidth < ignoreWidth || realHeight < ignoreHeight) + continue; + if (realWidth < MinWidth || realHeight < MinHeight) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, "尺寸过滤" + sizeStr.toStdString(), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + continue; + } + if (realArea < MinArea) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, "面积过滤" + sizeStr.toStdString(), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + continue; + } + bool clsIsNg = false; + cv::Mat clsImg = cutLabelImg(afterDingWei, 128, 128, rect, 1, 1, 0); + auto clsResult = _detParams->cls->Evaluate(clsImg); + double prob = clsResult.prob; + std::string defectName; + switch (clsResult.classes) { + case 0: { + defectName = "OK"; + break; + } + case 1: { + if (prob > clsProbThreshold) { + cv::Mat mask = segResult(rect); + cv::Scalar grayValue = cv::mean(afterDingWei(rect), mask); + if (grayValue[0] < 175) + clsIsNg = true; + } + defectName = "破损"; + break; + } + case 2: { + defectName = "脏污"; + break; + } + case 3: { + defectName = "褶皱"; + break; + } + case 4: { + defectName = "贴胶"; + break; + } + case 5: { + defectName = "其他"; + break; + } + default: { + defectName = "未知缺陷"; + break; + } + } + if (clsIsNg == true) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 0, 255), 3); + textPainter1.putText(Paint, defectName + sizeStr.toStdString() + "_r" + std::to_string(clsResult.classes) + "_p" + std::to_string(clsResult.prob), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 0, 255)); + isNg = true; + } + else { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, defectName + sizeStr.toStdString() + "_r" + std::to_string(clsResult.classes) + "_p" + std::to_string(clsResult.prob), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + } + data->insertClsOriginImg({ std::make_pair("result" + std::to_string(clsResult.classes) + "_prob" + std::to_string(clsResult.prob),clsImg) }); + //data->clsOriginImgs.push_back({ "result" + std::to_string(clsResult) + "_prob" + std::to_string(prob) , clsImg }); + //clsImgs.push_back({ "result" + std::to_string(clsResult) + "_prob" + std::to_string(prob) , clsImg }); + } + //data->insertClsOriginImg(clsImgs); + //data->service->_missionResultHandle->addSqlData(QString::fromStdString(data->SNcode), { {"包膜上大面结果", isNg } }); + if (isNg == true) { + textPainter.putText(Paint, "NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + defects.push_back("大面NG"); + } + else { + textPainter.putText(Paint, "OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 50; + TextRendererZH textPainterx(Paint.cols, Paint.rows, Rate); + textPainterx.putText(Paint, "产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainterx.putText(Paint, "获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + data->insertResultImg(Paint); + //data->insertResultImg(Paint); + + if (isNg) { + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + plcResult = "2"; + } + else + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + if (data->service->_paramSyncManager->get(objectName, "判定启用", false).toBool() == false) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); + + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "大面检测异常"); + + return true; +} + +class _RuanLianJie_damianjiance : public mission { +public: + _581_SX_Single_triggerTemplate< RuanLianJie_damianjiance> dibu_1; + _581_SX_Single_triggerTemplate< RuanLianJie_damianjiance> dibu_2; + _581_SX_Single_triggerTemplate< RuanLianJie_damianjiance> dibu_3; + _581_SX_Single_triggerTemplate< RuanLianJie_damianjiance> dibu_4; + + RuanLianJie_damianjiance DetParams1; + RuanLianJie_damianjiance DetParams2; + RuanLianJie_damianjiance DetParams3; + RuanLianJie_damianjiance DetParams4; + + bool initOnce = true; + void onConfigReloaded() { + if (initOnce) { + DetParams1.dingwei_seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/DaMianDingWei_Seg.BHD", 16, 0, "大面Seg定位网"); + DetParams2.dingwei_seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/DaMianDingWei_Seg.BHD", 16, 0, "大面Seg定位网"); + DetParams1.seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/DaMian_Seg.BHD", 16, 0, "大面Seg缺陷网"); + DetParams1.cls = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/DaMian_Cls.BHD", 1, 0, "大面Cls缺陷网"); + DetParams2.seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/DaMian_Seg.BHD", 16, 0, "大面Seg缺陷网"); + DetParams2.cls = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/DaMian_Cls.BHD", 1, 0, "大面Cls缺陷网"); + + DetParams3.dingwei_seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/DaMianDingWei_Seg.BHD", 16, 1, "大面Seg定位网"); + DetParams4.dingwei_seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/DaMianDingWei_Seg.BHD", 16, 1, "大面Seg定位网"); + DetParams3.seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/DaMian_Seg.BHD", 16, 1, "大面Seg缺陷网"); + DetParams3.cls = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/DaMian_Cls.BHD", 1, 1, "大面Cls缺陷网"); + DetParams4.seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/DaMian_Seg.BHD", 16, 1, "大面Seg缺陷网"); + DetParams4.cls = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/DaMian_Cls.BHD", 1, 1, "大面Cls缺陷网"); + initOnce = false; + } + } + bool initCustom() { +#pragma region 注册触发器回调 + dibu_1.init("Camera1", "大面检测-A通道-相机1(外)", { { 410, 1,{424},"412","460",470},{ 410, 2,{424},"412","461",472} }, 1, &DetParams1, _serviceManager, bridge, BaoMoQiangDaMian_Test); + dibu_2.init("Camera2", "大面检测-A通道-相机2(内)", { { 510, 1,{524},"512","560",570},{ 510, 2,{524},"512","561",572} }, 1, &DetParams2, _serviceManager, bridge, BaoMoQiangDaMian_Test); + dibu_3.init("Camera3", "大面检测-B通道-相机3(外)", { { 910, 1,{924},"912","960",970},{ 910, 2,{924},"912","961",972} }, 1, &DetParams3, _serviceManager, bridge, BaoMoQiangDaMian_Test); + dibu_4.init("Camera4", "大面检测-B通道-相机4(内)", { { 1310, 1,{1324},"1312","1360",1370},{ 1310, 2,{1324},"1312","1361",1372} }, 1, &DetParams4, _serviceManager, bridge, BaoMoQiangDaMian_Test); + +#pragma endregion + return true; + } +}; + +#pragma once \ No newline at end of file diff --git a/src/mission/581-Dgh-CB-1/CMakeLists.txt b/src/mission/604/RLJ/604_RuanLianJie_damianjiance/CMakeLists.txt similarity index 100% rename from src/mission/581-Dgh-CB-1/CMakeLists.txt rename to src/mission/604/RLJ/604_RuanLianJie_damianjiance/CMakeLists.txt diff --git a/src/mission/581_YuHan_hanqian/581_YuHan_hanqian.cpp b/src/mission/604/RLJ/604_RuanLianJie_dinggai/604_RuanLianJie_dinggai.cpp similarity index 76% rename from src/mission/581_YuHan_hanqian/581_YuHan_hanqian.cpp rename to src/mission/604/RLJ/604_RuanLianJie_dinggai/604_RuanLianJie_dinggai.cpp index 3874af8..4e6f82b 100644 --- a/src/mission/581_YuHan_hanqian/581_YuHan_hanqian.cpp +++ b/src/mission/604/RLJ/604_RuanLianJie_dinggai/604_RuanLianJie_dinggai.cpp @@ -1,8 +1,9 @@ -#include <581_YuHan_hanqian.h> +#include <604_RuanLianJie_dinggai.h> mission* createMission() { return new _581_YuHan_hanqian(); } + void destroyMission(mission* _mission) { delete _mission; } \ No newline at end of file diff --git a/src/mission/604/RLJ/604_RuanLianJie_dinggai/604_RuanLianJie_dinggai.h b/src/mission/604/RLJ/604_RuanLianJie_dinggai/604_RuanLianJie_dinggai.h new file mode 100644 index 0000000..cc0fa0a --- /dev/null +++ b/src/mission/604/RLJ/604_RuanLianJie_dinggai/604_RuanLianJie_dinggai.h @@ -0,0 +1,784 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} +using namespace HDevEngineCpp; +#pragma endregion + +struct rljFbfDistanceInput { + HObject in_Image; + HTuple in_Resolution; + HTuple in_FilePath; +}; + +struct rljFbfDistanceOutput { + HTuple out_LinePoint; + HTuple out_Circle; + HTuple out_Flag; + HTuple out_Distance; +}; + +struct rljJzDistanceInput { + HObject in_Image1; + HObject in_Image2; + HTuple in_Resolution; + HTuple in_FilePath; +}; + +struct rljJzDistanceOutput { + HTuple out_Edgedistance; + HTuple out_Distance; + HTuple out_LinePoint; + HTuple out_Flag; +}; + +struct rljMeltingPointInput { + HObject in_Image; + HTuple in_Resolution; +}; + +struct rljMeltingPointOutput { + HTuple out_Circle; + HTuple out_CCDistance; + HTuple out_X; + HTuple out_Y; + HTuple out_Areas; + HTuple out_Flag; + HTuple out_NgDetails; +}; + +static std::shared_ptr>& rljFbfDistanceEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_Cal_FBF_Distance", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljFbfDistanceInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljFbfDistanceOutput& out) { + out.out_LinePoint = outputHTuple["out_LinePoint"]; + out.out_Circle = outputHTuple["out_Circle"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_Distance = outputHTuple["out_Distance"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& rljJzDistanceEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_Cal_JZ_Distance", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljJzDistanceInput& in) { + inputHObj["in_Image1"] = in.in_Image1; + inputHObj["in_Image2"] = in.in_Image2; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljJzDistanceOutput& out) { + out.out_Edgedistance = outputHTuple["out_Edgedistance"]; + out.out_Distance = outputHTuple["out_Distance"]; + out.out_LinePoint = outputHTuple["out_LinePoint"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& rljMeltingPointEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_MeltingPoint_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljMeltingPointInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Resolution"] = in.in_Resolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljMeltingPointOutput& out) { + out.out_Circle = outputHTuple["out_Circle"]; + out.out_CCDistance = outputHTuple["out_CCDistance"]; + out.out_X = outputHTuple["out_X"]; + out.out_Y = outputHTuple["out_Y"]; + out.out_Areas = outputHTuple["out_Areas"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + }); + return instance; + }(); + return evaluater; +} + +class RuanLianJie_dinnggai { +public: + handdleAI::pSegmentation* segDingwei1 = nullptr; + handdleAI::pSegmentation* seg = nullptr; + handdleAI::pClassification* cls = nullptr; + cv::Mat TP1IMG, TP2Paint; + QByteArray kemaResult = "1"; +}; + +cv::Rect getImageLocateRect(cv::Mat& src, handdleAI::pSegmentation* seg, double bounus = 0.1) { + cv::Mat tmpImg; + cv::resize(src, tmpImg, cv::Size(), bounus, bounus); + cv::Mat ouput = ~seg->Evaluate(tmpImg); + cv::Rect rect = findMaxAreaContour(ouput); + rect.x /= bounus; rect.y /= bounus; + rect.width /= bounus; rect.height /= bounus; + return rect; +} + +bool dinggaijiance(std::shared_ptr data, RuanLianJie_dinnggai* _detParams, sx_581_address address) { + try { + + bool jizhuEnable = data->service->_paramSyncManager->get("防爆阀检测", "启用极柱检测(须与程序沟通)", false).toBool(); + std::vectordefects; + double Resolution = 0.01424; + cv::Mat sourceImg = data->getOriImg(0); + //sourceImg = cv::imread("D:\\testImage\\qr" + std::to_string(address.triggerValue) + ".bmp", -1); // 测试 + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + putSnAndDetTime(Paint, data->SNcode, 200); + if (address.triggerValue == 2)//kema + { + rljFbfDistanceInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_Resolution = Resolution; + hInput.in_FilePath = ("./halconFiles/" + data->cameraKeyName + "/").c_str(); + auto hOutput = rljFbfDistanceEvaluater()->evaluate(hInput); + double keMaShangBianYuanLimitHigh = data->service->_paramSyncManager->get("刻码检测", "刻码上边缘到防爆阀距离(上限)mm", 5.00).toDouble(); + double keMaShangBianYuanLimitLow = data->service->_paramSyncManager->get("刻码检测", "刻码上边缘到防爆阀距离(下限)mm", 3.00).toDouble(); + bool keMaShangBianYuanEnable= data->service->_paramSyncManager->get("刻码检测", "刻码上边缘到防爆阀距离判定启用", false).toBool(); + + double qrCodeSizeLimitHigh = data->service->_paramSyncManager->get("刻码检测", "二维码尺寸(上限)mm", 9.30).toDouble(); + double qrCodeSizeLimitLow = data->service->_paramSyncManager->get("刻码检测", "二维码尺寸(下限)mm", 8.70).toDouble(); + bool qrCodeSizeEnable = data->service->_paramSyncManager->get("刻码检测", "二维码判定启用", false).toBool(); + + double LimitHigh2 = data->service->_paramSyncManager->get("防爆阀检测", "右边缘到防爆阀圆心(上限)mm", 2.00).toDouble(); + double LimitLow2 = data->service->_paramSyncManager->get("防爆阀检测", "右边缘到防爆阀圆心(下限)mm", 0.00).toDouble(); + bool Enable2 = data->service->_paramSyncManager->get("防爆阀检测", "右边缘到防爆阀圆心判定启用", false).toBool(); + + cvPainter textPainter(140); + if (hOutput.runFlag && hOutput.result.out_Flag.D() == 1) + { + auto& halconOutput = hOutput.result; + int crossLineLength = 50; + + for (int i = 0; i < halconOutput.out_LinePoint.Length() / 4; i++) + { + textPainter.appendPaintJob(cv::Point(halconOutput.out_LinePoint[i * 4 + 1].D(), halconOutput.out_LinePoint[i * 4].D()), + cv::Point(halconOutput.out_LinePoint[i * 4 + 3].D(), halconOutput.out_LinePoint[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 10); + } + double distance = halconOutput.out_Distance[0].D() + data->service->_paramSyncManager->get("防爆阀检测", QString::fromStdString(data->cameraKeyName) + "右边缘补偿mm", 0.00).toDouble(); + textPainter.appendPaintJob(cv::Point(halconOutput.out_Circle[1].D(), halconOutput.out_Circle[0].D()), halconOutput.out_Circle[2].D(), cv::Scalar(0, 255, 0), 5, 0); + { + std::string text = "二维码右竖直边和防爆阀圆心距离最小距离:" + std::to_string(halconOutput.out_Distance[0].D()); + text = text.substr(0, text.find(".") + 4) + "mm"; + if (distance > LimitHigh2) {//NG + defects.push_back("二维码右距离NG"); + data->halconOK = false; + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 500), cv::Scalar(0, 0, 255)); + if (Enable2 == true) + _detParams->kemaResult = "2"; + } + else {//OK + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 500), cv::Scalar(0, 255, 0)); + } + + } + double distance2 = halconOutput.out_Distance[1].D() + data->service->_paramSyncManager->get("防爆阀检测", QString::fromStdString(data->cameraKeyName) + "上边缘补偿mm", 0.00).toDouble();//二维码的上边缘到防爆阀下边缘的距离 + { + QByteArray::number(distance2); + std::string text = "二维码的上边缘到防爆阀下边缘的距离:" + std::to_string(distance2); + text = text.substr(0, text.find(".") + 4) + "mm"; + if (distance2 > keMaShangBianYuanLimitHigh || distance2 < keMaShangBianYuanLimitLow) {//NG + defects.push_back("二维码上距离NG"); + data->halconOK = false; + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 650), cv::Scalar(0, 0, 255)); + if (keMaShangBianYuanEnable == true) + _detParams->kemaResult = "2"; + } + else {//OK + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 650), cv::Scalar(0, 255, 0)); + } + } + bool sizeNg = false; + double distance3 = halconOutput.out_Distance[2].D();//二维码下方字符串的宽度值 + { + std::string text = "二维码的宽度值:" + std::to_string(distance3); + text = text.substr(0, text.find(".") + 4) + "mm"; + if (distance3 > qrCodeSizeLimitHigh || distance3 < qrCodeSizeLimitLow) {//NG + sizeNg = true; + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 800), cv::Scalar(0, 0, 255)); + } + else { + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 800), cv::Scalar(0, 255, 0)); + } + + } + double distance4 = halconOutput.out_Distance[2].D();//二维码下方字符串的宽度值 + { + std::string text = "二维码的高度值:" + std::to_string(distance4); + text = text.substr(0, text.find(".") + 4) + "mm"; + if (distance4 > qrCodeSizeLimitHigh || distance4 < qrCodeSizeLimitLow) {//NG + sizeNg = true; + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 950), cv::Scalar(0, 0, 255)); + } + else { + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 950), cv::Scalar(0, 255, 0)); + } + } + + if (sizeNg == true) { + defects.push_back("二维码高度NG"); + data->halconOK = false; + if (qrCodeSizeEnable == true) + _detParams->kemaResult = "2"; + } + } + else + { + qDebug() << __FUNCTION__ << "line:" << __LINE__ << "二维码error"; + cvPainter textPainter2(500); + textPainter2.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + textPainter2.executePaintJobs(Paint); + _detParams->kemaResult = "2";//暫存結果 + data->halconOK = false; + + } + //#endif + if (jizhuEnable == false) { + if (_detParams->kemaResult == "2") + data->halconOK = false; + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + _detParams->kemaResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + _detParams->kemaResult);//结果反馈 + _detParams->kemaResult = "1";//fuwei + textPainter.executePaintJobs(Paint); + data->insertResultImg(Paint); + } + else { + textPainter.executePaintJobs(Paint); + Paint.copyTo(_detParams->TP2Paint); + } + } + + else if (address.triggerValue == 1)//jizhu1 + { + sourceImg.copyTo(_detParams->TP1IMG); + } + + else if (address.triggerValue == 3)//jizhu2 + { + //提前发结果 + if (_detParams->kemaResult == "2") + data->halconOK = false; + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + _detParams->kemaResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + _detParams->kemaResult);//结果反馈 + _detParams->kemaResult = "1";//fuwei + // + Paint = cv::Mat(_detParams->TP1IMG.rows, _detParams->TP1IMG.cols * 3, CV_8UC1); + _detParams->TP1IMG.copyTo(Paint(cv::Rect(0, 0, _detParams->TP1IMG.cols, _detParams->TP1IMG.rows))); + sourceImg.copyTo(Paint(cv::Rect(_detParams->TP1IMG.cols, 0, _detParams->TP1IMG.cols, _detParams->TP1IMG.rows))); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, 8); + _detParams->TP2Paint.copyTo(Paint(cv::Rect(_detParams->TP1IMG.cols * 2, 0, _detParams->TP1IMG.cols, _detParams->TP1IMG.rows))); + cv::Mat PaintPart2 = Paint(cv::Rect(_detParams->TP1IMG.cols, 0, _detParams->TP1IMG.cols, _detParams->TP1IMG.rows)); + rljJzDistanceInput hInput; + hInput.in_Image1 = MatToHObject(_detParams->TP1IMG); + hInput.in_Image2 = MatToHObject(sourceImg); + hInput.in_Resolution = Resolution; + hInput.in_FilePath = ("./halconFiles/" + data->cameraKeyName + "/").c_str(); + auto hOutput = rljJzDistanceEvaluater()->evaluate(hInput); + if (hOutput.runFlag && hOutput.result.out_Flag.D() == 1) + { + auto& halconOutput = hOutput.result; + for (int i = 0; i < 4; i++) + { + cv::line(Paint, cv::Point(halconOutput.out_LinePoint[i * 4 + 1].D(), halconOutput.out_LinePoint[i * 4].D()), + cv::Point(halconOutput.out_LinePoint[i * 4 + 3].D(), halconOutput.out_LinePoint[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 15); + } + for (int i = 4; i < 8; i++) + { + cv::line(PaintPart2, cv::Point(halconOutput.out_LinePoint[i * 4 + 1].D(), halconOutput.out_LinePoint[i * 4].D()), + cv::Point(halconOutput.out_LinePoint[i * 4 + 3].D(), halconOutput.out_LinePoint[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 15); + } + double Distance = halconOutput.out_Distance.D(); + cvPainter textPainter(280); + //Paint + { + std::string text = "垂直间距:" + std::to_string(halconOutput.out_Edgedistance[0].D()); + //data3 = QByteArray::number(out_Edgedistance[0].D()); + text = text.substr(0, text.find(".") + 4) + "mm"; + textPainter.appendPaintJob((text).c_str(), cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + { + //data4 = QByteArray::number(out_Edgedistance[1].D()); + std::string text = "水平间距:" + std::to_string(halconOutput.out_Edgedistance[1].D()); + text = text.substr(0, text.find(".") + 4) + "mm"; + textPainter.appendPaintJob((text).c_str(), cv::Point(50, 300), cv::Scalar(0, 255, 0)); + } + { + //data5 = QByteArray::number(out_Edgedistance[2].D()); + std::string text = "垂直间距:" + std::to_string(halconOutput.out_Edgedistance[2].D()); + text = text.substr(0, text.find(".") + 4) + "mm"; + textPainter.appendPaintJob((text).c_str(), cv::Point(PaintPart2.cols + 50, 50), cv::Scalar(0, 255, 0)); + } + { + //data6 = QByteArray::number(out_Edgedistance[3].D()); + std::string text = "水平间距:" + std::to_string(halconOutput.out_Edgedistance[3].D()); + text = text.substr(0, text.find(".") + 4) + "mm"; + textPainter.appendPaintJob((text).c_str(), cv::Point(PaintPart2.cols + 50, 300), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + } + /*{ + std::string text = "极柱间距:" + std::to_string(Distance); + text = text.substr(0, text.find(".") + 4) + "mm"; + putTextHusky(Paint, + utf8_to_gbk(text).c_str(), + cv::Point(50, 550), cv::Scalar(0, 0, 255), 280); + putTextHusky(PaintPart2, + utf8_to_gbk(text).c_str(), + cv::Point(50, 550), cv::Scalar(0, 0, 255), 280); + }*/ + + } + else + { + data->halconOK = false; + cvPainter textPainter1(500); + textPainter1.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + textPainter1.executePaintJobs(Paint); + } + } + sourceImg.release(); + + if (address.triggerValue == 3) { + data->insertResultImg(Paint); + } + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(500); + textPainter.appendPaintJob("NG", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; +} + +bool dinggaijiance_FBF(std::shared_ptr data, RuanLianJie_dinnggai* _detParams, sx_581_address address) { + QByteArray plcResult = "1"; + try { + bool sizeNg = false; + bool distanceNg = false; + bool jizhuEnable = data->service->_paramSyncManager->get("防爆阀检测", "启用极柱检测(须与程序沟通)", false).toBool(); + double pixSize = data->service->_paramSyncManager->get("防爆阀检测", QString::fromStdString(data->cameraKeyName) + "像素精度(mm/pix)", 0.0203304).toDouble(); + std::vectordefects; + double Resolution = pixSize; + cv::Mat sourceImg = data->getOriImg(0); + if(true == data->service->_paramSyncManager->get("hide", "刻码后离线测试", false).toBool()) { + sourceImg = cv::imread("D:\\testImages\\" + data->cameraKeyName + ".bmp", -1); // 测试 + data->service->_paramSyncManager->edit("hide", "刻码后离线测试", false); + } + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + putSnAndDetTime(Paint, data->SNcode, 200); + rljFbfDistanceInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_Resolution = Resolution; + hInput.in_FilePath = ("./halconFiles/" + data->cameraKeyName + "/").c_str(); + auto hOutput = rljFbfDistanceEvaluater()->evaluate(hInput); + double keMaShangBianYuanLimitHigh = data->service->_paramSyncManager->get("刻码检测", "刻码上边缘到防爆阀距离(上限)mm", 5.00).toDouble(); + double keMaShangBianYuanLimitLow = data->service->_paramSyncManager->get("刻码检测", "刻码上边缘到防爆阀距离(下限)mm", 3.00).toDouble(); + bool keMaShangBianYuanEnable = data->service->_paramSyncManager->get("刻码检测", "刻码上边缘到防爆阀距离判定启用", false).toBool(); + + double qrCodeSizeLimitHigh = data->service->_paramSyncManager->get("刻码检测", "二维码尺寸(上限)mm", 9.30).toDouble(); + double qrCodeSizeLimitLow = data->service->_paramSyncManager->get("刻码检测", "二维码尺寸(下限)mm", 8.70).toDouble(); + bool qrCodeSizeEnable = data->service->_paramSyncManager->get("刻码检测", "二维码判定启用", false).toBool(); + + double LimitHigh2 = data->service->_paramSyncManager->get("防爆阀检测", "右边缘到防爆阀圆心(上限)mm", 2.00).toDouble(); + double LimitLow2 = data->service->_paramSyncManager->get("防爆阀检测", "右边缘到防爆阀圆心(下限)mm", 0.00).toDouble(); + bool Enable2 = data->service->_paramSyncManager->get("防爆阀检测", "右边缘到防爆阀圆心判定启用", false).toBool(); + + cvPainter textPainter(140); + if (hOutput.runFlag && hOutput.result.out_Flag.D() == 1) { + auto& halconOutput = hOutput.result; + int crossLineLength = 50; + + for (int i = 0; i < halconOutput.out_LinePoint.Length() / 4; i++) { + textPainter.appendPaintJob(cv::Point(halconOutput.out_LinePoint[i * 4 + 1].D(), halconOutput.out_LinePoint[i * 4].D()), + cv::Point(halconOutput.out_LinePoint[i * 4 + 3].D(), halconOutput.out_LinePoint[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 10); + } + double distance = halconOutput.out_Distance[0].D() + data->service->_paramSyncManager->get("防爆阀检测", QString::fromStdString(data->cameraKeyName) + "右边缘补偿mm", 0.00).toDouble(); + textPainter.appendPaintJob(cv::Point(halconOutput.out_Circle[1].D(), halconOutput.out_Circle[0].D()), halconOutput.out_Circle[2].D(), cv::Scalar(0, 255, 0), 5, 0); + { + std::string text = "二维码右竖直边和防爆阀圆心距离最小距离:" + std::to_string(halconOutput.out_Distance[0].D()); + text = text.substr(0, text.find(".") + 4) + "mm"; + if (distance > LimitHigh2) {//NG + defects.push_back("二维码右距离NG"); + data->halconOK = false; + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 50), cv::Scalar(0, 0, 255)); + if (Enable2 == true) { + plcResult = "2"; + } + } + else {//OK + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 50), cv::Scalar(0, 255, 0)); + } + + } + double distance2 = halconOutput.out_Distance[1].D() + data->service->_paramSyncManager->get("防爆阀检测", QString::fromStdString(data->cameraKeyName) + "上边缘补偿mm", 0.00).toDouble();//二维码的上边缘到防爆阀下边缘的距离 + { + QByteArray::number(distance2); + std::string text = "二维码的上边缘到防爆阀下边缘的距离:" + std::to_string(distance2); + text = text.substr(0, text.find(".") + 4) + "mm"; + if (distance2 > keMaShangBianYuanLimitHigh || distance2 < keMaShangBianYuanLimitLow) {//NG + defects.push_back("二维码上距离NG"); + data->halconOK = false; + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 200), cv::Scalar(0, 0, 255)); + if (keMaShangBianYuanEnable == true){ + plcResult = "2"; + } + } + else {//OK + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 200), cv::Scalar(0, 255, 0)); + } + } + + double distance3 = halconOutput.out_Distance[2].D();//二维码下方字符串的宽度值 + { + std::string text = "二维码的宽度值:" + std::to_string(distance3); + text = text.substr(0, text.find(".") + 4) + "mm"; + if (distance3 > qrCodeSizeLimitHigh || distance3 < qrCodeSizeLimitLow) {//NG + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 350), cv::Scalar(0, 0, 255)); + sizeNg = true; + if(qrCodeSizeEnable == true){ + plcResult = "2"; + } + } + else { + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 350), cv::Scalar(0, 255, 0)); + } + + } + double distance4 = halconOutput.out_Distance[3].D();//二维码下方字符串的宽度值 + { + std::string text = "二维码的高度值:" + std::to_string(distance4); + text = text.substr(0, text.find(".") + 4) + "mm"; + if (distance4 > qrCodeSizeLimitHigh || distance4 < qrCodeSizeLimitLow) {//NG + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 500), cv::Scalar(0, 0, 255)); + sizeNg = true; + if (qrCodeSizeEnable == true) { + plcResult = "2"; + } + } + else { + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 500), cv::Scalar(0, 255, 0)); + } + } + + double distance5 = halconOutput.out_Distance[4].D();//水平字符的宽度 + { + std::string text = "水平字符的宽度值:" + std::to_string(distance5); + text = text.substr(0, text.find(".") + 4) + "mm"; + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 650), cv::Scalar(0, 255, 0)); + } + + double distance6 = halconOutput.out_Distance[5].D();//垂直字符的高度值 + { + std::string text = "垂直字符的高度值:" + std::to_string(distance6); + text = text.substr(0, text.find(".") + 4) + "mm"; + textPainter.appendPaintJob((text).c_str(), cv::Point(100, 800), cv::Scalar(0, 255, 0)); + } + } + else { + qDebug() << __FUNCTION__ << "line:" << __LINE__ << "二维码error"; + cvPainter textPainter2(500); + textPainter2.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + textPainter2.executePaintJobs(Paint); + data->halconOK = false; + plcResult = "2"; + } + + if (sizeNg) { + + } + + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + + std::string paintAddr = std::to_string(address.triggerAddr) + "_" + std::to_string(address.triggerValue); + textPainter.appendPaintJob(("pos:" + paintAddr).c_str(), cv::Point(100, Paint.rows - 600), cv::Scalar(0, 255, 0)); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//发送结果 + + textPainter.executePaintJobs(Paint); + data->insertResultImg(Paint); + + sourceImg.release(); + + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(500); + textPainter.appendPaintJob("NG", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; +} + +bool majiarerong(std::shared_ptr data, RuanLianJie_dinnggai * _detParams, sx_581_address address) { + try { + std::vectordefects; + double areaLimitHigh = data->service->_paramSyncManager->get("马甲热熔", "热熔面积mm²(上限)", 22.5).toDouble(); + double areaLimitLow = data->service->_paramSyncManager->get("马甲热熔", "热熔面积mm²(下限)", 7.5).toDouble(); + bool areaEnable = data->service->_paramSyncManager->get("马甲热熔", "热熔面积启用", false).toBool(); + + double tongTxdLimitHigh = data->service->_paramSyncManager->get("马甲热熔", "铜同心度(上限)", 2.0).toDouble(); + double lvTxdLimitHigh = data->service->_paramSyncManager->get("马甲热熔", "铝同心度(上限)", 2.0).toDouble(); + bool maJiaTxdEnable = data->service->_paramSyncManager->get("马甲热熔", "同心度判定启用", false).toBool(); + + int reRongCountLimit = data->service->_paramSyncManager->get("马甲热熔", "热熔点数(下限)", 8).toInt(); + bool reRongCountEnable = data->service->_paramSyncManager->get("马甲热熔", "热熔点数判定启用", false).toBool(); + cv::Mat Paint; + cv::Mat src = data->getOriImg(0); + if (true == data->service->_paramSyncManager->get("hide", "马甲热熔离线测试", false).toBool()) { + src = cv::imread("D:\\testImages\\" + data->cameraKeyName + ".bmp", -1); // 测试 + data->service->_paramSyncManager->edit("hide", "马甲热熔离线测试", false); + } + src.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + putSnAndDetTime(Paint, data->SNcode, 200); + + rljMeltingPointInput hInput; + hInput.in_Image = MatToHObject(src); + hInput.in_Resolution = 0.0425339; + auto hOutput = rljMeltingPointEvaluater()->evaluate(hInput); + int flag = hOutput.runFlag ? hOutput.result.out_Flag.D() : 0; + bool isAreaNg = false, isCountNg = false, txdNg = false;; + QByteArray plcResult = "1", dianShuResult = "1", areaResult = "1"; + cvPainter textPainter(180); + auto& halconOutput = hOutput.result; + int l = halconOutput.out_Circle.Length(), l2 = halconOutput.out_X.Length(); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5);//缩小结果图 + cvPainter reRongTextPainter(80); + if (flag == 1) { + for (int i = 0; i < halconOutput.out_Circle.Length() / 3; i++) { + cv::circle(Paint, cv::Point(halconOutput.out_Circle[i * 3 + 1].D(), halconOutput.out_Circle[i * 3].D()) / 2, halconOutput.out_Circle[i * 3 + 2].D() / 2, cv::Scalar(0, 255, 0), 2); + } + for (int i = 0; i < halconOutput.out_X.Length() && i < halconOutput.out_Y.Length(); i++) { + Paint.at(halconOutput.out_Y[i].D() / 2, halconOutput.out_X[i].D() / 2) = cv::Vec3b(0, 0, 255); + } + double txd_up = halconOutput.out_CCDistance[0].D(), txd_down = halconOutput.out_CCDistance[1].D();//一般上面为铜,下面为铝 + +#pragma region 同心度判定 + if (txd_up > tongTxdLimitHigh) {//NG + reRongTextPainter.appendPaintJob("铜极同心度:" + std::to_string(txd_up), cv::Point(0, 200), cv::Scalar(0, 0, 255)); + txdNg = true; + } + else { + reRongTextPainter.appendPaintJob("铜极同心度:" + std::to_string(txd_up), cv::Point(0, 200), cv::Scalar(0, 255, 0)); + } + + if (txd_down > lvTxdLimitHigh) {//NG + reRongTextPainter.appendPaintJob("铝极同心度:" + std::to_string(txd_down), cv::Point(0, 280), cv::Scalar(0, 0, 255)); + txdNg = true; + } + else { + reRongTextPainter.appendPaintJob("铝极同心度:" + std::to_string(txd_down), cv::Point(0, 280), cv::Scalar(0, 255, 0)); + } + + if (txdNg == true) { + defects.push_back("热熔同心度NG"); + data->halconOK = false; + if (maJiaTxdEnable == true) + plcResult = "2"; + } +#pragma endregion +#pragma region 热熔点数判定 + int reRongCount = halconOutput.out_Areas.Length(); + if (reRongCount < reRongCountLimit) { + reRongTextPainter.appendPaintJob("热熔点数:" + std::to_string(reRongCount), cv::Point(1900, 50), cv::Scalar(0, 0, 255)); + defects.push_back("热熔点数NG"); + data->halconOK = false; + isCountNg = true; + if (reRongCountEnable) { + plcResult = "2"; + dianShuResult = "2"; + } + } + else + reRongTextPainter.appendPaintJob("热熔点数:" + std::to_string(reRongCount), cv::Point(1900, 50), cv::Scalar(0, 255 , 0)); +#pragma endregion + + for (int i = 0; i < halconOutput.out_Areas.Length(); i++) { + double area = halconOutput.out_Areas[i].D(); + std::string valueText = std::to_string(halconOutput.out_Areas[i].D()); + valueText.substr(0, valueText.find(".") + 2); + if (area > areaLimitHigh || area < areaLimitLow) {//NG + reRongTextPainter.appendPaintJob("热熔点面积" + std::to_string(i + 1) + ":" + valueText, cv::Point(0, 400 + i * 80), cv::Scalar(0, 0, 255)); + defects.push_back("热熔面积NG"); + data->halconOK = false; + isAreaNg = true; + if (areaEnable) { + plcResult = "2"; + areaResult = "2"; + } + } + else { + reRongTextPainter.appendPaintJob("热熔点面积" + std::to_string(i + 1) + ":" + valueText, cv::Point(0, 400 + i * 80), cv::Scalar(0, 255, 0)); + } + } + } + else { + plcResult = "2"; + isCountNg = true; + data->halconOK = false; + std::string defectStr = hOutput.runFlag ? gbkToUtf8(halconOutput.out_NgDetails.ToString().Text()) : hOutput.faildResult; + if(defectStr.length() > 2) + defectStr = defectStr.substr(1, defectStr.length() - 2); + textPainter.appendPaintJob(defectStr, cv::Point(0, 500), cv::Scalar(0, 255, 0)); + defects.push_back(defectStr); + } + + if (isCountNg == true || isAreaNg == true || txdNg == true) { + textPainter.appendPaintJob("NG", cv::Point(0, 0), cv::Scalar(0, 0, 255)); + data->halconOK = false; + } + else + textPainter.appendPaintJob("OK", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + + "_" + areaResult //铜热熔面积 + + ",0," + dianShuResult //铜热熔点数 + +",0," + areaResult +",0,"//铝热熔面积 + + dianShuResult); //铝热熔点数 + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + //data->setNg(false, !data->halconOK, defects); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + reRongTextPainter.appendPaintJob((areaEnable == true ? "热熔面积判定启用:是" : "热熔面积判定启用:否"), cv::Point(1900, 300), areaEnable == true ? cv::Scalar(0, 255, 0): cv::Scalar(0, 255, 255)); + reRongTextPainter.appendPaintJob((maJiaTxdEnable == true ? "热熔同心度判定启用:是" : "热熔同心度判定启用:否"), cv::Point(1900, 400), maJiaTxdEnable == true ? cv::Scalar(0, 255, 0) : cv::Scalar(0, 255, 255)); + reRongTextPainter.appendPaintJob((reRongCountEnable == true ? "热熔点数判定启用:是" : "热熔点数判定启用:否"), cv::Point(1900, 500), reRongCountEnable == true ? cv::Scalar(0, 255, 0) : cv::Scalar(0, 255, 255)); + reRongTextPainter.executePaintJobs(Paint); + textPainter.executePaintJobs(Paint); + data->insertResultImg(Paint); + return true; + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(500); + textPainter.appendPaintJob("NG", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + QByteArray plcResult ="1"; + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_"+ plcResult);//铜热熔面积 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_1");//铜热熔点数 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_1");//铝热熔面积 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 6) + "_1");//铝热熔点数 + return true; +} + +class _581_YuHan_hanqian : public mission { +public: + //_581_SX_Single_triggerTemplate< detParams> dibu_1; + //_581_SX_Single_triggerTemplate< detParams> dibu_2; + + _581_SX_Single_triggerTemplate< RuanLianJie_dinnggai> dibu_1; + _581_SX_Single_triggerTemplate< RuanLianJie_dinnggai> dibu_2; + + _581_SX_Single_triggerTemplate< RuanLianJie_dinnggai> dibu_3; + _581_SX_Single_triggerTemplate< RuanLianJie_dinnggai> dibu_4; + + + + //detParams DetParams1; + //detParams DetParams2; + RuanLianJie_dinnggai DetParams1; + RuanLianJie_dinnggai DetParams2; + + RuanLianJie_dinnggai DetParams3; + RuanLianJie_dinnggai DetParams4; + + + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + + } + bool initCustom() { +#pragma region 注册触发器回调 + dibu_1.init("Camera1", "顶盖检测刻码后-相机1", { { 5000, 1,{5010},"5002","5004",-1},{ 5000, 2,{5010},"5002","5004",-1},{ 5000, 3,{5010},"5002","5004",5030} }, 1, &DetParams1, _serviceManager, bridge, dinggaijiance_FBF, "防爆阀CCD"); + dibu_2.init("Camera2", "顶盖检测刻码后-相机2", { { 5100, 1,{5110},"5102","5104",-1},{ 5100, 2,{5110},"5102","5104",-1},{ 5100, 3,{5110},"5102","5104",5130} }, 1, &DetParams2, _serviceManager, bridge, dinggaijiance_FBF, "防爆阀CCD"); + dibu_3.init("Camera3", "马甲热熔检测-相机1", { { 5200, 1,{5210},"5202","5204",5230 }}, 1, &DetParams3, _serviceManager, bridge, majiarerong, "马甲热熔CCD"); + dibu_4.init("Camera4", "马甲热熔检测-相机2", { { 5300, 1,{5310},"5302","5304",5330} }, 1, &DetParams4, _serviceManager, bridge, majiarerong, "马甲热熔CCD"); +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/581-Mfd_yh_mh_hh/CMakeLists.txt b/src/mission/604/RLJ/604_RuanLianJie_dinggai/CMakeLists.txt similarity index 100% rename from src/mission/581-Mfd_yh_mh_hh/CMakeLists.txt rename to src/mission/604/RLJ/604_RuanLianJie_dinggai/CMakeLists.txt diff --git a/src/mission/604/RLJ/604_RuanLianJie_majiatiejiao/604_RuanLianJie_majiatiejiao.cpp b/src/mission/604/RLJ/604_RuanLianJie_majiatiejiao/604_RuanLianJie_majiatiejiao.cpp new file mode 100644 index 0000000..a0bc9aa --- /dev/null +++ b/src/mission/604/RLJ/604_RuanLianJie_majiatiejiao/604_RuanLianJie_majiatiejiao.cpp @@ -0,0 +1,9 @@ +#include <604_RuanLianJie_majiatiejiao.h> + +mission* createMission() { + return new _581_YuHan_hanqian(); +} + +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/RLJ/604_RuanLianJie_majiatiejiao/604_RuanLianJie_majiatiejiao.h b/src/mission/604/RLJ/604_RuanLianJie_majiatiejiao/604_RuanLianJie_majiatiejiao.h new file mode 100644 index 0000000..85386eb --- /dev/null +++ b/src/mission/604/RLJ/604_RuanLianJie_majiatiejiao/604_RuanLianJie_majiatiejiao.h @@ -0,0 +1,231 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +class RuanLianJie_majiatiejiao { +public: + +}; + +struct rljMajiaInput { + HObject in_Image; + HTuple in_Resolution; +}; + +struct rljMajiaOutput { + HTuple out_ErrRectangle; + HTuple out_GlueRectangle; + HTuple out_Flag; + HTuple out_NgDetails; + HTuple out_Distance; + HTuple out_LinePoint; +}; + +static std::shared_ptr>& rljMajiaEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_Inspection_Packaging_Tape", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljMajiaInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Resolution"] = in.in_Resolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljMajiaOutput& out) { + out.out_ErrRectangle = outputHTuple["out_ErrRectangle"]; + out.out_GlueRectangle = outputHTuple["out_GlueRectangle"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + out.out_Distance = outputHTuple["out_Distance"]; + out.out_LinePoint = outputHTuple["out_LinePoint"]; + }); + return instance; + }(); + return evaluater; +} + +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 50, const std::string& extraText = "") { + TextRendererZH textPainter(Paint.cols, Paint.rows, textSize); + textPainter.putText(Paint, "SN:" + sn + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, 0), cv::Scalar(0, 255, 0)); +} +bool tiejiaojianece(std::shared_ptr data, RuanLianJie_majiatiejiao* _detParams, sx_581_address address) { + + float out_Distance_1 = -999; + float out_Distance_2 = -999; + + try { + + float inResolution = 1.0; + if (data->cameraKeyName == "Camera1") { + inResolution = data->service->_paramSyncManager->get("像素精度", "A面顶部相机像素精度", 1.0).toFloat(); + } + if (data->cameraKeyName == "Camera2") { + inResolution = data->service->_paramSyncManager->get("像素精度", "B面顶部相机像素精度", 1.0).toFloat(); + } + if (data->cameraKeyName == "Camera3") { + inResolution = data->service->_paramSyncManager->get("像素精度", "A面底部相机像素精度", 1.0).toFloat(); + } + if (data->cameraKeyName == "Camera4") { + inResolution = data->service->_paramSyncManager->get("像素精度", "A面底部相机2像素精度", 1.0).toFloat(); + } + if (data->cameraKeyName == "Camera5") { + inResolution = data->service->_paramSyncManager->get("像素精度", "B面底部相机1像素精度", 1.0).toFloat(); + } + if (data->cameraKeyName == "Camera6") { + inResolution = data->service->_paramSyncManager->get("像素精度", "B面底部相机2像素精度", 1.0).toFloat(); + //data->oriImgs[0] = cv::imread("D:\\testImage\\" + data->cameraKeyName + ".bmp",-1); + } + auto 马甲贴胶边缘到盖板间距 = genValueControlStruct("马甲贴胶", "马甲贴胶边缘到盖板间距", "", 500, 0, false, data->service->_paramSyncManager); + + //存图数据加保存队列,加锁,避免数据访问冲突 + QByteArray plcResult = "2"; + cv::Mat sourceImg = data->getOriImg(0); + + //sourceImg = cv::imread("E:\\testImages\\60A01MB5601FB2H225832_Camera1_ORI_0_15-35-04-088.bmp", -1); + + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + putSnAndDetTime(Paint, data->SNcode, 200); + rljMajiaInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_Resolution = inResolution; + auto hOutput = rljMajiaEvaluater()->evaluate(hInput); + if (!hOutput.runFlag) { + throw std::runtime_error(hOutput.faildResult); + } + auto& halconOutput = hOutput.result; + + int flag = halconOutput.out_Flag.D(); + plcResult = "1"; + cvPainter textPainter(500); + cvPainter textPainter_1(200); + std::vector defects; + if (flag == 1) + { + int out_GlueRectangle_L = halconOutput.out_GlueRectangle.Length(); + for (auto i = 0; i < out_GlueRectangle_L / 4; i++) + textPainter.appendPaintJob( + cv::Rect(halconOutput.out_GlueRectangle[i * 4], halconOutput.out_GlueRectangle[i * 4 + 1], + halconOutput.out_GlueRectangle[i * 4 + 2], halconOutput.out_GlueRectangle[i * 4 + 3]), + cv::Scalar(0, 255, 0), 5); + } + else + { + int out_ErrRectangle_L = halconOutput.out_ErrRectangle.Length(); + for (auto i = 0; i < out_ErrRectangle_L / 4; i++) + textPainter.appendPaintJob( + cv::Rect(halconOutput.out_ErrRectangle[i * 4], halconOutput.out_ErrRectangle[i * 4 + 1], + halconOutput.out_ErrRectangle[i * 4 + 2], halconOutput.out_ErrRectangle[i * 4 + 3]), + cv::Scalar(0, 0, 255), 10); + + std::string NgDeTails = gbkToUtf8(halconOutput.out_NgDetails.ToString().Text()); + textPainter.appendPaintJob(NgDeTails, cv::Point(0, 550), cv::Scalar(0, 0, 255)); + data->halconOK = false; + plcResult = "2"; + if (data->cameraKeyName == "Camera1" || data->cameraKeyName == "Camera4") + defects.push_back("顶部贴胶NG"); + else + defects.push_back("底部贴胶NG"); + } + + for (int m = 0; m < halconOutput.out_LinePoint.Length() / 4; m++) { + textPainter.appendPaintJob( + cv::Point(halconOutput.out_LinePoint[m * 4 + 0].D(), halconOutput.out_LinePoint[m * 4 + 1].D()), \ + cv::Point(halconOutput.out_LinePoint[m * 4 + 2].D(), halconOutput.out_LinePoint[m * 4 + 3].D()), \ + cv::Scalar(0, 255, 0), 10); + } + + if(halconOutput.out_Distance.Length() == 2) { + out_Distance_1 = halconOutput.out_Distance[0].D(); + out_Distance_2 = halconOutput.out_Distance[1].D(); + valueJudgement(out_Distance_1, 马甲贴胶边缘到盖板间距, 0, data->halconOK, + "底部绿胶带到底座的距离1:", "", "底部绿胶带到底座的距离NG", cv::Point(0, 1000), defects, textPainter); + valueJudgement(out_Distance_2, 马甲贴胶边缘到盖板间距, 0, data->halconOK, + "底部绿胶带到底座的距离2:", "", "底部绿胶带到底座的距离NG", cv::Point(0, 1200), defects, textPainter); + if (!data->halconOK) + plcResult = "2"; + } + + if(plcResult == "2") + textPainter.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + else + textPainter.appendPaintJob("OK", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + + if (data->service->_paramSyncManager->get("通用设置", QString::fromStdString(data->cameraKeyName) + "强制OK", false).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + \ + QByteArray::number(out_Distance_1) + "," + QByteArray::number(out_Distance_2));//发送数据 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + putSnAndDetTime(Paint, data->SNcode, 50); + textPainter.executePaintJobs(Paint); + textPainter_1.executePaintJobs(Paint); + data->insertResultImg(Paint); + + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + TextRendererZH textPainter(Paint.cols, Paint.rows, 500); + textPainter.putText(Paint, "NG", cv::Point(0, 0), cv::Scalar(0, 0, 255)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + \ + QByteArray::number(out_Distance_1) + "," + QByteArray::number(out_Distance_2));//发送数据 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + return true; +} +class _581_YuHan_hanqian : public mission { +public: + _581_SX_Single_triggerTemplate< RuanLianJie_majiatiejiao> dibu_1; + _581_SX_Single_triggerTemplate< RuanLianJie_majiatiejiao> dibu_2; + _581_SX_Single_triggerTemplate< RuanLianJie_majiatiejiao> dibu_3; + _581_SX_Single_triggerTemplate< RuanLianJie_majiatiejiao> dibu_4; + _581_SX_Single_triggerTemplate< RuanLianJie_majiatiejiao> dingbu_1; + _581_SX_Single_triggerTemplate< RuanLianJie_majiatiejiao> dingbu_2; + + RuanLianJie_majiatiejiao DetParams; + //RuanLianJie_majiatiejiao lvDetParams; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + } + bool initCustom() { +#pragma region 注册触发器回调 + dingbu_1.init("Camera1", "马甲贴胶检测-A面-顶部相机-相机1", { { 5000, 1,{5014},"5002","5006",5050} }, 1, &DetParams, _serviceManager, bridge, tiejiaojianece); + dingbu_2.init("Camera2", "马甲贴胶检测-B面-顶部相机-相机1", { { 5100, 1,{5114},"5102","5106",5150} }, 1, &DetParams, _serviceManager, bridge, tiejiaojianece); + dibu_1.init("Camera3", "马甲贴胶检测-A面-底部相机-相机1", { { 5200, 1,{5214},"5202","5206",5250} }, 1, &DetParams, _serviceManager, bridge, tiejiaojianece); + dibu_2.init("Camera4", "马甲贴胶检测-A面-底部相机-相机2", { { 5300, 1,{5314},"5302","5306",5350} }, 1, &DetParams, _serviceManager, bridge, tiejiaojianece); + dibu_3.init("Camera5", "马甲贴胶检测-B面-底部相机-相机1", { { 5400, 1,{5414},"5402","5406",5450} }, 1, &DetParams, _serviceManager, bridge, tiejiaojianece); + dibu_4.init("Camera6", "马甲贴胶检测-B面-底部相机-相机2", { { 5500, 1,{5514},"5502","5506",5550} }, 1, &DetParams, _serviceManager, bridge, tiejiaojianece); + + + +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/581_Ruanlianjie_dianjiao_tiejiao/CMakeLists.txt b/src/mission/604/RLJ/604_RuanLianJie_majiatiejiao/CMakeLists.txt similarity index 100% rename from src/mission/581_Ruanlianjie_dianjiao_tiejiao/CMakeLists.txt rename to src/mission/604/RLJ/604_RuanLianJie_majiatiejiao/CMakeLists.txt diff --git a/src/mission/604/RLJ/604_Ruanlianjie_dianjiao_tiejiao/604_Ruanlianjie_dianjiao_tiejiao.cpp b/src/mission/604/RLJ/604_Ruanlianjie_dianjiao_tiejiao/604_Ruanlianjie_dianjiao_tiejiao.cpp new file mode 100644 index 0000000..3f31aa1 --- /dev/null +++ b/src/mission/604/RLJ/604_Ruanlianjie_dianjiao_tiejiao/604_Ruanlianjie_dianjiao_tiejiao.cpp @@ -0,0 +1,520 @@ +#include <604_Ruanlianjie_dianjiao_tiejiao.h> +#include +#define swtichToNew 1 + +struct rljDianJiaoGlueInput { + HObject in_Image; + HTuple in_Resolution; + HTuple in_FilePath; + HTuple in_ProductType; +}; + +struct rljDianJiaoGlueOutput { + HTuple out_Circle; + HTuple out_GlueArea; + HTuple out_X; + HTuple out_Y; + HTuple out_Flag; + HTuple out_Ratio; + HTuple out_NgTails; +}; + +struct rljDianJiaoHeightInput { + HObject in_Image1; + HObject in_Image2; + HTuple in_Resolution; +}; + +struct rljDianJiaoHeightOutput { + HTuple out_LinePoint; + HTuple out_Rows; + HTuple out_Cols; + HTuple out_Height; + HTuple out_Flag; +}; + +struct rljDianJiaoCoverInput { + HObject in_Image; + HTuple in_FilePath; + HTuple in_ProductType; + HTuple in_Resolution; +}; + +struct rljDianJiaoCoverOutput { + HTuple out_LinePoint; + HTuple out_NgDetails; + HTuple out_X; + HTuple out_Y; + HTuple out_Flag; +}; + +static std::shared_ptr>& rljDianJiaoGlueEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_Inspection_Glue", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljDianJiaoGlueInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + inputHTuple["in_ProductType"] = in.in_ProductType; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljDianJiaoGlueOutput& out) { + out.out_Circle = outputHTuple["out_Circle"]; + out.out_GlueArea = outputHTuple["out_GlueArea"]; + out.out_X = outputHTuple["out_X"]; + out.out_Y = outputHTuple["out_Y"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_Ratio = outputHTuple["out_Ratio"]; + out.out_NgTails = outputHTuple["out_NgTails"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& rljDianJiaoHeightEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("Cal_Glue_Height_3_2", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljDianJiaoHeightInput& in) { + inputHObj["in_Image1"] = in.in_Image1; + inputHObj["in_Image2"] = in.in_Image2; + inputHTuple["in_Resolution"] = in.in_Resolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljDianJiaoHeightOutput& out) { + out.out_LinePoint = outputHTuple["out_LinePoint"]; + out.out_Rows = outputHTuple["out_Rows"]; + out.out_Cols = outputHTuple["out_Cols"]; + out.out_Height = outputHTuple["out_Height"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& rljDianJiaoCoverEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_Inspection_GlueCover", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljDianJiaoCoverInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_FilePath"] = in.in_FilePath; + inputHTuple["in_ProductType"] = in.in_ProductType; + inputHTuple["in_Resolution"] = in.in_Resolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljDianJiaoCoverOutput& out) { + out.out_LinePoint = outputHTuple["out_LinePoint"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + out.out_X = outputHTuple["out_X"]; + out.out_Y = outputHTuple["out_Y"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + return instance; + }(); + return evaluater; +} + +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} + +QByteArray getCameraIndex(QString cameraStr) { + QRegularExpression re("\\d+"); + auto match = re.match(cameraStr); + if (match.hasMatch()) { + return "_" + match.captured(0).toLocal8Bit(); + } + return "_-2"; +} +void exceptionResume(std::shared_ptr data, tieJiaoParams* _detParams, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob(why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("通用设置","强制OK", false).toBool() == true) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 +} +void exceptionResume(std::shared_ptr data, dianJiaoParams* _detParams, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob(why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("通用设置" ,"强制OK", false).toBool() == true) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 +} +struct dianjiaoHalconResult { + QByteArray plcResult = "1"; + bool isRateNg = false; + bool isAreaNg = false; + bool isAlgoNg = false; +}; +dianjiaoHalconResult halconAsyncFuncGlueArea(cvPainter* render, std::shared_ptr data, cv::Mat& sourceImg, double Resolution, std::string ProductType) { + dianjiaoHalconResult result; + double areaNgRate = data->service->_paramSyncManager->get("点胶设置","胶体比例最小阈值(%)", 90.0).toDouble(); + double areaNgRateEnable = data->service->_paramSyncManager->get("点胶设置","胶体比例判定启用", true).toBool(); + double areaNgSizeLow = data->service->_paramSyncManager->get("点胶设置", "胶体面积最小阈值(mm²)", 80.0).toDouble(); + double areaNgSizeHigh = data->service->_paramSyncManager->get("点胶设置", "胶体面积最大阈值(mm²)",130.0).toDouble(); + double areaNgSizeEnable = data->service->_paramSyncManager->get("点胶设置", "胶体面积判定启用", true).toBool(); + double areaRateOffset = data->service->_paramSyncManager->get("点胶设置", "胶体比例补偿(%)", 0.0).toDouble(); + double areaSizeffset = data->service->_paramSyncManager->get("点胶设置", "胶体面积补偿(mm²)", 0.0).toDouble(); + QElapsedTimer timer; + timer.start(); + rljDianJiaoGlueInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_Resolution = Resolution; + hInput.in_FilePath = "./halconFiles/dianJiao/"; + hInput.in_ProductType = ProductType.c_str(); + auto hOutput = rljDianJiaoGlueEvaluater()->evaluate(hInput); + qDebug() << "dianjiao " << timer.elapsed(); + if (!hOutput.runFlag) { + render->appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + render->appendPaintJob(hOutput.faildResult, cv::Point(0, 550), cv::Scalar(0, 0, 255)); + result.isAlgoNg = true; + if (areaNgRateEnable == true || areaNgSizeEnable == true) + result.plcResult = "2"; + data->halconOK = false; + return result; + } + auto& halconOutput = hOutput.result; + + if (halconOutput.out_Flag.D() == 1) + { + //int offsetY = 0; + cv::Point circleCenter(halconOutput.out_Circle[1].D(), halconOutput.out_Circle[0].D()); + double radius = halconOutput.out_Circle[2].D(); + double glueArea = halconOutput.out_GlueArea.D() * Resolution * Resolution + areaSizeffset; + double rate = halconOutput.out_Ratio.D() + areaRateOffset / 100; + rate = std::min(rate, 1.0); + render->appendPaintJob(circleCenter, radius, cv::Scalar(0, 0, 255), 3,0); + + //cv::Point gluePos(out_X.D(), out_Y[0].D()); + for (int i = 0; i < halconOutput.out_X.Length() || i < halconOutput.out_Y.Length(); i++) + render->appendPaintJob(cv::Point(halconOutput.out_X[i].D(), halconOutput.out_Y[i].D()), cv::Scalar(0, 0, 255)); + + if (rate * 100 < areaNgRate) {//NG + render->appendPaintJob("胶体比例:" + std::to_string(rate * 100) + "%", cv::Point(0, 450), cv::Scalar(0, 0, 255)); + result.isRateNg = true; + data->halconOK = false; + if (areaNgRateEnable == true) + result.plcResult = "2"; + } + else{ + render->appendPaintJob("胶体比例:" + std::to_string(rate * 100) + "%", cv::Point(0, 450), cv::Scalar(0, 255, 0)); + } + + if (glueArea > areaNgSizeHigh || glueArea < areaNgSizeLow) {//NG + render->appendPaintJob("胶体面积:" + std::to_string(glueArea) + "mm²", cv::Point(0, 150), cv::Scalar(0, 0, 255)); + result.isAreaNg = true; + data->halconOK = false; + if(areaNgSizeEnable == true) + result.plcResult = "2"; + } + else//OK + render->appendPaintJob("胶体面积:" + std::to_string(glueArea) + "mm²", cv::Point(0, 150), cv::Scalar(0, 255, 0)); + } + else + { + render->appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + render->appendPaintJob(gbkToUtf8(halconOutput.out_NgTails.ToString().Text()), cv::Point(0, 550), cv::Scalar(0, 0, 255)); + result.isAlgoNg = true; + if (areaNgRateEnable == true || areaNgSizeEnable == true) + result.plcResult = "2"; + data->halconOK = false; + } + return result; +} +QByteArray halconAsyncFuncGlueHeight(std::shared_ptr data,cv::Mat& Paint, cv::Mat& lastImg, cv::Mat& sourceImg, double Resolution) { + lastImg(cv::Rect(0, 700, sourceImg.cols, sourceImg.rows - 1400)).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + rljDianJiaoHeightInput hInput; + hInput.in_Image1 = MatToHObject(sourceImg); + hInput.in_Image2 = MatToHObject(lastImg); + hInput.in_Resolution = Resolution; + auto hOutput = rljDianJiaoHeightEvaluater()->evaluate(hInput); + if (!hOutput.runFlag) { + cvPainter textPainter(500); + textPainter.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + textPainter.executePaintJobs(Paint); + return "1"; + } + auto& halconOutput = hOutput.result; + + QByteArray plcResult = "1"; + if (halconOutput.out_Flag.D() == 1) + { + int offsetY = 700; + cv::Point p1 = cv::Point(halconOutput.out_LinePoint[1].D(), halconOutput.out_LinePoint[0].D() - offsetY); + cv::Point p2 = cv::Point(halconOutput.out_LinePoint[3].D(), halconOutput.out_LinePoint[2].D() - offsetY); + cv::line(Paint, p1, p2, cv::Scalar(0, 255, 0), 5); + cvPainter textPainter(300); + textPainter.appendPaintJob("胶体高于基准面的距离:" + std::to_string(halconOutput.out_Height[0].D()), cv::Point(0, 50), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("胶体低于基准面的距离:" + std::to_string(halconOutput.out_Height[1].D()), cv::Point(0, 350), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + for (int i = 0; i < halconOutput.out_Rows.Length(); i++) + Cross(Paint, cv::Point(halconOutput.out_Cols[i].D(), halconOutput.out_Rows[i].D() - offsetY), 5, 3, cv::Scalar(0, 255, 0)); + plcResult = "1"; + } + else + { + cvPainter textPainter(500); + textPainter.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + textPainter.executePaintJobs(Paint); + plcResult = "1"; + } + return plcResult; +} +bool DianJiaoJianCe(std::shared_ptr data, dianJiaoParams* _detParams, sx_581_address address){ + try + { + std::vector defects; + auto cameraIndexStr = getCameraIndex(QString::fromStdString(data->cameraKeyName)); + cv::Mat sourceImg = data->getOriImg(0); + + if (sourceImg.rows == 200) { + exceptionResume(data, _detParams, address, "取图超时"); + return true; + } + if (address.triggerValue == 1 || address.triggerValue == 2) {//铜 + cv::Rect rect(data->service->_paramSyncManager->get("点胶裁切", "铜裁切起点X", 850).toInt(), + data->service->_paramSyncManager->get("点胶裁切", "铜裁切起点Y", 500).toInt(), + data->service->_paramSyncManager->get("点胶裁切", "铜裁切宽度", 3644).toInt(), + data->service->_paramSyncManager->get("点胶裁切", "铜裁切高度", 2476).toInt()); + //sourceImg = cv::imread("D:\\testImg\\T.bmp", -1); + sourceImg(rect).copyTo(sourceImg); + } + else {//铝 + cv::Rect rect(data->service->_paramSyncManager->get("点胶裁切", "铝裁切起点X", 850).toInt(), + data->service->_paramSyncManager->get("点胶裁切", "铝裁切起点Y", 500).toInt(), + data->service->_paramSyncManager->get("点胶裁切", "铝裁切宽度", 3644).toInt(), + data->service->_paramSyncManager->get("点胶裁切", "铝裁切高度", 2476).toInt()); + //sourceImg = cv::imread("D:\\testImg\\L.bmp", -1); + sourceImg(rect).copyTo(sourceImg); + } + QByteArray plcResult = "1"; + qDebug() << "algo start" << address.triggerAddr; + + double Resolution = data->service->_paramSyncManager->get("点胶", QString::fromStdString(data->cameraKeyName) + "像素精度", 0.0233).toDouble(); + + cvPainter textPainter(300); + /* if (address.triggerAddr == 110 || address.triggerAddr == 210) + offsetArea = 0.1 ;*/ + dianjiaoHalconResult dianJiaoResult; + if (address.triggerValue == 2) { + //检测胶 + dianJiaoResult = halconAsyncFuncGlueArea(&textPainter,data, sourceImg, Resolution, "CU"); + textPainter.appendPaintJob( std::to_string(address.triggerAddr) + "_" + std::to_string(address.triggerValue), cv::Point(100, 2000), cv::Scalar(0, 255, 0)); + plcResult = dianJiaoResult.plcResult; + if (dianJiaoResult.isAlgoNg == true) + defects.push_back("点胶算法NG"); + if(dianJiaoResult.isAreaNg == true) + defects.push_back("点胶面积NG"); + if (dianJiaoResult.isRateNg == true) + defects.push_back("点胶比例NG"); + } + else if (address.triggerValue == 4) { + //检测胶 + dianJiaoResult = halconAsyncFuncGlueArea(&textPainter,data, sourceImg, Resolution, "AL"); + textPainter.appendPaintJob(std::to_string(address.triggerAddr) + "_" + std::to_string(address.triggerValue), cv::Point(100, 2000), cv::Scalar(0, 255, 0)); + plcResult = dianJiaoResult.plcResult; + if(dianJiaoResult.isAlgoNg == true) + defects.push_back("点胶算法NG"); + if (dianJiaoResult.isAreaNg == true) + defects.push_back("点胶面积NG"); + if (dianJiaoResult.isRateNg == true) + defects.push_back("点胶比例NG"); + } + + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool())//如果总强制开启使用总强制 + plcResult = "1"; + else if (data->service->_paramSyncManager->get("点胶设置", "点胶检测强制OK", false).toBool())//否则使用下级强制 + plcResult = "1"; + if (address.triggerValue == 2) {//第一/三次的结果需要缓存 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.resultAddr.toInt() + 1) + "_" + _detParams->cacheResult + "," + plcResult); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + QByteArray(_detParams->lastResultIsNg == true || plcResult == "2" ? "2" : "1")); + + _detParams->batteryIsNg = (_detParams->lastResultIsNg == true || plcResult == "2"); + _detParams->lastResultIsNg = false; + } + else if (address.triggerValue == 4) { + + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.resultAddr.toInt() + 3) + "_" + _detParams->cacheResult + "," + plcResult); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + QByteArray(_detParams->lastResultIsNg == true || plcResult == "2" ? "2" : "1")); + if (_detParams->batteryIsNg == true) + data->halconOK = false; + + _detParams->lastResultIsNg = false; + _detParams->batteryIsNg = false; + } + else { + _detParams->cacheResult = plcResult; + _detParams->lastResultIsNg = (plcResult == "2"); + } + //data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_1"); + + cv::Mat Paint; + sourceImg.copyTo(Paint); + putSnAndDetTime(Paint, data->SNcode, 80); + if (address.triggerValue == 2) {//第一/三次的结果需要缓存 + textPainter.executePaintJobs(Paint); + Paint.copyTo(_detParams->lastDetImg); + } + else if (address.triggerValue == 4) { + textPainter.executePaintJobs(Paint); + cv::resize(_detParams->lastDetImg, _detParams->lastDetImg, Paint.size()); + if (_detParams->lastDetImg.empty() == false) + cv::hconcat(_detParams->lastDetImg, Paint, Paint); + data->insertResultImg(Paint); + } + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + qDebug() << "algo stop" << address.triggerAddr; + + return true; + } + CATCH_EXCEPTIONS + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + exceptionResume(data, _detParams, address, "检测失败"); + return false; + +} +int testIndex = 0; +bool TieJiaoJianCe(std::shared_ptr data, tieJiaoParams* _detParams, sx_581_address address) { + try { + auto cameraIndexStr = getCameraIndex(QString::fromStdString(data->cameraKeyName)); + cv::Mat sourceImg; + + //if (address.triggerAddr == 810) + // cv::rotate(data->oriImgs[0], data->oriImgs[0], cv::ROTATE_180); + sourceImg = data->getOriImg(0); + + /*if (true == data->service->_paramSyncManager->get("hide", "离线测试", false).toBool()) { + std::string readImagePath ="D:/testImages/"+ data->cameraKeyName + "_" + std::to_string(address.triggerValue) + ".bmp"; + sourceImg = cv::imread(readImagePath, -1); + data->service->_paramSyncManager->edit("hide", "离线测试", false); + }*/ + + int start = clock(); + + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(800); + cvPainter textPainter2(400); + if (address.triggerValue == 1 || address.triggerValue == 3) { + _detParams->productType = "CU"; + textPainter2.appendPaintJob("铜", cv::Point(10, 900), cv::Scalar(0, 255, 0)); + } + else { + _detParams->productType = "LV"; + textPainter2.appendPaintJob("铝", cv::Point(10, 900), cv::Scalar(0, 255, 0)); + } + + QElapsedTimer timer; + timer.start(); + rljDianJiaoCoverInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_FilePath = "./halconFiles/tieJiao/"; + hInput.in_ProductType = _detParams->productType.toStdString().c_str(); + hInput.in_Resolution = 0.0241; + auto hOutput = rljDianJiaoCoverEvaluater()->evaluate(hInput); + qDebug() << "tiejiao " << timer.elapsed(); + if (!hOutput.runFlag) { + throw std::runtime_error(hOutput.faildResult); + } + auto& halconOutput = hOutput.result; + int flag = halconOutput.out_Flag.D(); + QByteArray plcResult = "1"; + + if (flag == 1) { + for (auto i = 0; i < halconOutput.out_X.Length() && i < halconOutput.out_Y.Length(); i++) { + textPainter2.appendPaintJob(cv::Point(halconOutput.out_X[i].D(), halconOutput.out_Y[i].D()),cv::Scalar(0, 255, 0)); + } + for (auto i = 0; i < 4; i++) { + textPainter2.appendPaintJob( + cv::Point(halconOutput.out_LinePoint[i * 4 + 1].D(), halconOutput.out_LinePoint[i * 4].D()), + cv::Point(halconOutput.out_LinePoint[i * 4 + 3].D(), halconOutput.out_LinePoint[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 5); + } + textPainter.appendPaintJob("OK", cv::Point(10, 100), cv::Scalar(0, 255, 0)); + } + else { + data->halconOK = false; + textPainter.appendPaintJob("NG", cv::Point(10, 100), cv::Scalar(0, 0, 255)); + textPainter2.appendPaintJob(gbkToUtf8(halconOutput.out_NgDetails.ToString().Text()), cv::Point(10, 800), cv::Scalar(0, 0, 255)); + if (data->service->_paramSyncManager->get("贴胶", "贴胶判定启用", false).toBool()) + plcResult = "2"; + } + + + if (data->service->_paramSyncManager->get("通用设置","强制OK", false).toBool()) + plcResult = "1"; + if (address.triggerValue == 2) { + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.resultAddr.toInt() + 1) + "_" + QByteArray(_detParams->lastResultIsNg == true ? "2" : "1") + "," + plcResult); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + QByteArray(_detParams->lastResultIsNg == true || plcResult == "2" ? "2" : "1")); + _detParams->lastResultIsNg = false; + } + else if (address.triggerValue == 4) { + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.resultAddr.toInt() + 3) + "_" + QByteArray(_detParams->lastResultIsNg == true ? "2" : "1") + "," + plcResult); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + QByteArray(_detParams->lastResultIsNg == true || plcResult == "2" ? "2" : "1")); + _detParams->lastResultIsNg = false; + } + else { + if (plcResult == "2") + _detParams->lastResultIsNg = true; + } + textPainter.executePaintJobs(Paint); + textPainter2.executePaintJobs(Paint); + data->insertResultImg(Paint); + putSnAndDetTime(Paint, data->SNcode, 80); + return true; + } + CATCH_EXCEPTIONS + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + exceptionResume(data, _detParams, address, "检测失败"); + return false; +} + +mission* createMission() { + return new _581_Ruanlianjie_dianjiao_tiejiao(); +} +void destroyMission(mission* _mission) { + delete _mission; +} diff --git a/src/mission/604/RLJ/604_Ruanlianjie_dianjiao_tiejiao/604_Ruanlianjie_dianjiao_tiejiao.h b/src/mission/604/RLJ/604_Ruanlianjie_dianjiao_tiejiao/604_Ruanlianjie_dianjiao_tiejiao.h new file mode 100644 index 0000000..9d1fc03 --- /dev/null +++ b/src/mission/604/RLJ/604_Ruanlianjie_dianjiao_tiejiao/604_Ruanlianjie_dianjiao_tiejiao.h @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +using namespace HalconCpp; +class dianJiaoParams { +public: + cv::Mat lastDetImg; + bool lastResultIsNg = false; + QByteArray cacheResult = "1";//第一/三次的结果需要缓存 + bool batteryIsNg = false; +}; +class tieJiaoParams { +public: + QString productType; + bool lastResultIsNg = false; + QByteArray cacheResult = "1";//第一/三次的结果需要缓存 +}; + +bool DianJiaoJianCe(std::shared_ptr data, dianJiaoParams* _detParams, sx_581_address address); +bool TieJiaoJianCe(std::shared_ptr data, tieJiaoParams* _detParams, sx_581_address address); + + + +class _581_Ruanlianjie_dianjiao_tiejiao : public mission { +public: + + _581_SX_Single_triggerTemplate dianjiao_a_hou_1; + _581_SX_Single_triggerTemplate dianjiao_a_qian_2; + _581_SX_Single_triggerTemplate dianjiao_b_hou_3; + _581_SX_Single_triggerTemplate dianjiao_b_qian_4; + + _581_SX_Single_triggerTemplate tiejiao_a_1; + _581_SX_Single_triggerTemplate tiejiao_b_2; + + std::vector _dianJiaoParams = std::vector(4); + tieJiaoParams tieJiaoParams1; + tieJiaoParams tieJiaoParams2; + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + + if (initOnce == false) { + + initOnce = true; + } + } + bool initCustom() { +#pragma region 注册触发器回调 + dianjiao_a_hou_1.init("Camera1", "点胶检测-A通道-相机1(左)", { + { 110, 1,{124},"112","160",170}, + { 110, 2,{124},"112","160",170}, + { 110, 3,{124},"112","160",170}, + { 110, 4,{124},"112","160",170} }, 1, & _dianJiaoParams[0], _serviceManager, bridge, DianJiaoJianCe, "点胶检测CCD"); + dianjiao_a_qian_2.init("Camera2", "点胶检测-A通道-相机2(右)", { + { 210, 1,{224},"212","260",270}, + { 210, 2,{224},"212","260",270}, + { 210, 3,{224},"212","260",270}, + { 210, 4,{224},"212","260",270} }, 1, & _dianJiaoParams[1], _serviceManager, bridge, DianJiaoJianCe,"点胶检测CCD"); + dianjiao_b_hou_3.init("Camera3", "点胶检测-B通道-相机3(左)", { + { 610, 1,{624},"612","660",670}, + { 610, 2,{624},"612","660",670}, + { 610, 3,{624},"612","660",670}, + { 610, 4,{624},"612","660",670} }, 1, & _dianJiaoParams[2], _serviceManager, bridge, DianJiaoJianCe, "点胶检测CCD"); + dianjiao_b_qian_4.init("Camera4", "点胶检测-B通道-相机4(右)", { + { 710, 1,{724},"712","760",770}, + { 710, 2,{724},"712","760",770}, + { 710, 3,{724},"712","760",770}, + { 710, 4,{724},"712","760",770} }, 1, & _dianJiaoParams[3], _serviceManager, bridge, DianJiaoJianCe,"点胶检测CCD"); + + tiejiao_a_1.init("Camera5", "贴胶检测-A通道-相机1", { + { 310, 1,{324},"312","360",370}, + { 310, 2,{324},"312","360",370}, + { 310, 3,{324},"312","360",370}, + { 310, 4,{324},"312","360",370} }, 1, &tieJiaoParams1, _serviceManager, bridge, TieJiaoJianCe, "贴胶检测CCD"); + tiejiao_b_2.init("Camera6", "贴胶检测-B通道-相机2", { + { 810, 1,{824},"812","860",870}, + { 810, 2,{824},"812","860",870} , + { 810, 3,{824},"812","860",870} , + { 810, 4,{824},"812","860",870} }, 1, &tieJiaoParams2, _serviceManager, bridge, TieJiaoJianCe,"贴胶检测CCD"); +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/581_Ruanlianjie_hanqian_hanhou/CMakeLists.txt b/src/mission/604/RLJ/604_Ruanlianjie_dianjiao_tiejiao/CMakeLists.txt similarity index 100% rename from src/mission/581_Ruanlianjie_hanqian_hanhou/CMakeLists.txt rename to src/mission/604/RLJ/604_Ruanlianjie_dianjiao_tiejiao/CMakeLists.txt diff --git a/src/mission/604/RLJ/604_Ruanlianjie_hanqian_hanhou/604_Ruanlianjie_hanqian_hanhou.cpp b/src/mission/604/RLJ/604_Ruanlianjie_hanqian_hanhou/604_Ruanlianjie_hanqian_hanhou.cpp new file mode 100644 index 0000000..3c6da8e --- /dev/null +++ b/src/mission/604/RLJ/604_Ruanlianjie_hanqian_hanhou/604_Ruanlianjie_hanqian_hanhou.cpp @@ -0,0 +1,830 @@ +#include <604_Ruanlianjie_hanqian_hanhou.h> +#include +#define switchToNew 1 + +struct rljHanQianDwInput { + HObject in_Image; + HTuple in_Resolution; + HTuple in_FilePath; +}; + +struct rljHanQianDwOutput { + HTuple out_ColorType; + HTuple out_Distance; + HTuple out_CCDistance; + HTuple out_Flag; + HTuple out_NgTails; + HObject out_DisplayImage; +}; + +struct rljHanHou2DInput { + HObject in_Image; + HObject in_ImageSeg; + HTuple in_FliePath; + HTuple in_Resolution; +}; + +struct rljHanHou2DOutput { + HTuple out_Distance; + HTuple out_Circle; + HTuple out_Flag; + HTuple out_NgTails; + HTuple out_Angle; +}; + +struct rljHanHou3DInput { + HObject in_ImageH; + HTuple in_XResolution; + HTuple in_YResolution; + HTuple in_ZResolution; + HTuple in_FilePath; +}; + +struct rljHanHou3DOutput { + HTuple out_Height; + HTuple out_Ellipse; + HTuple out_Flag; + HTuple out_NGDetails; +}; + +static std::shared_ptr>& rljHanQianDwEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_HQDW_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljHanQianDwInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljHanQianDwOutput& out) { + out.out_ColorType = outputHTuple["out_ColorType"]; + out.out_Distance = outputHTuple["out_Distance"]; + out.out_CCDistance = outputHTuple["out_CCDistance"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgTails = outputHTuple["out_NgTails"]; + out.out_DisplayImage = outputHObj["out_DisplayImage"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& rljHanHou2DEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_Cal_HY_CC_Distance", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljHanHou2DInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHObj["in_ImageSeg"] = in.in_ImageSeg; + inputHTuple["in_FliePath"] = in.in_FliePath; + inputHTuple["in_Resolution"] = in.in_Resolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljHanHou2DOutput& out) { + out.out_Distance = outputHTuple["out_Distance"]; + out.out_Circle = outputHTuple["out_Circle"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgTails = outputHTuple["out_NgTails"]; + out.out_Angle = outputHTuple["out_Angle"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& rljHanHou3DEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_3_Inspection_HY_Defect", + [](std::map& inputHObj, + std::map& inputHTuple, + const rljHanHou3DInput& in) { + inputHObj["in_ImageH"] = in.in_ImageH; + inputHTuple["in_XResolution"] = in.in_XResolution; + inputHTuple["in_YResolution"] = in.in_YResolution; + inputHTuple["in_ZResolution"] = in.in_ZResolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + rljHanHou3DOutput& out) { + out.out_Height = outputHTuple["out_Height"]; + out.out_Ellipse = outputHTuple["out_Ellipse"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NGDetails = outputHTuple["out_NGDetails"]; + }); + return instance; + }(); + return evaluater; +} + +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn, cv::Point(0, Paint.rows - textSize * 2), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob(QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} + +QByteArray getCameraIndex(QString cameraStr) { + QRegularExpression re("\\d+"); + auto match = re.match(cameraStr); + if (match.hasMatch()) { + return "_" + match.captured(0).toLocal8Bit(); + } + return "_-2"; +} +void exceptionResume(std::shared_ptr data, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob(why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + + else + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + +} +struct halconResult_2D { + bool isNg = false; + double TongXingDu = -99; + double neiYuanPianYi_X = -99, neiYuanPianYi_Y = -99, neiYuanTongXinDu = -99; + double waiYuanPianYi_X = -99, waiYuanPianYi_Y = -99, waiYuanTongXinDu = -99; + bool halconOK = false; + std::string ngText = ""; +}; +bool HanQianDingWei(std::shared_ptr data, hanQianDingWeiParams* _detParams, sx_581_address address) { + try + { + bool isDebugMode = data->service->_paramSyncManager->get("hide", "调试模式", false).toBool(); + + double resolution = data->service->_paramSyncManager->get("焊前定位", QString::fromStdString(data->cameraKeyName) + "相机精度", 0.0355).toDouble(); + bool zuoLJPtoGaiBanEnable = data->service->_paramSyncManager->get("焊前定位", "左侧连接片和盖板距离判定启用", false).toBool(); + double zuoLJPtoGaiBanMaxThreshold = data->service->_paramSyncManager->get("焊前定位", "左侧连接片和盖板距离(MAX)", 1.8).toDouble(); + double zuoLJPtoGaiBanMinThreshold = data->service->_paramSyncManager->get("焊前定位", "左侧连接片和盖板距离(MIN)", 0.0).toDouble(); + + bool youLJPtoGaiBanEnable = data->service->_paramSyncManager->get("焊前定位", "右侧连接片和盖板距离判定启用", false).toBool(); + double youLJPtoGaiBanMaxThreshold = data->service->_paramSyncManager->get("焊前定位", "右侧连接片和盖板距离(MAX)", 1.8).toDouble(); + double youLJPtoGaiBanMinThreshold = data->service->_paramSyncManager->get("焊前定位", "右侧连接片和盖板距离(MIN)", 0.0).toDouble(); + std::vector defects; + QByteArray plcResult = "1"; + cv::Mat sourceImg = data->getOriImg(0); + //sourceImg = cv::imread("D://testImage//hqdw.bmp", -1); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + putSnAndDetTime(Paint, data->SNcode, 80); + rljHanQianDwInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_Resolution = resolution; + hInput.in_FilePath = "./halconFiles/hanqian/"; + auto hOutput = rljHanQianDwEvaluater()->evaluate(hInput); + if (!hOutput.runFlag) { + throw std::runtime_error(hOutput.faildResult); + } + auto& halconOutput = hOutput.result; + int flag = halconOutput.out_Flag.D(); + + // out_ColorType: 1=铜, 2=铝。 + // 当前工位的正确极性布局是左铝右铜。 + double value1_1 = 0, value1_2 = 0, value2_1 = 0, value2_2 = 0; + bool jiXingEnable = data->service->_paramSyncManager->get("焊前定位", "焊前极性判定启用", false).toBool(); + cvPainter textPainter(200); + cvPainter textPainter2(600); + cvPainter textPainter3(800); + if (flag == 1) + { + bool isNg = false; + + Paint = HObject2Mat(halconOutput.out_DisplayImage); + int l = halconOutput.out_Distance.Length(); + if (halconOutput.out_Distance.Length() >= 3) { + value1_1 = halconOutput.out_Distance[1].D(); + value1_2 = halconOutput.out_Distance[2].D(); + textPainter.appendPaintJob("线段夹角:" + std::to_string(halconOutput.out_Distance[3].D()) + "°", cv::Point(0, 10), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铜极+铝极连接片中心距离:" + std::to_string(halconOutput.out_Distance[0].D()) + "mm", cv::Point(0, sourceImg.rows - 950), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("电芯上水平边缘和注液孔的中心圆的距离:" + std::to_string(halconOutput.out_Distance[1].D()) + "mm", cv::Point(0, sourceImg.rows - 750), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("电芯下水平边缘和注液孔的中心圆的距离:" + std::to_string(halconOutput.out_Distance[2].D()) + "mm", cv::Point(0, sourceImg.rows - 550), cv::Scalar(0, 255, 0)); + + data->sqlVec.insert({ "电芯上水平边缘和注液孔的中心圆的距离", halconOutput.out_Distance[1].D() }); + data->sqlVec.insert({ "电芯下水平边缘和注液孔的中心圆的距离", halconOutput.out_Distance[2].D() }); + } + if (halconOutput.out_CCDistance.Length() == 2) { + value2_1 = halconOutput.out_CCDistance[0].D(); + if (zuoLJPtoGaiBanMaxThreshold < value2_1 || zuoLJPtoGaiBanMinThreshold > value2_1) {//NG + textPainter.appendPaintJob("左侧连接片圆心和盖板圆心的距离:" + std::to_string(value2_1) + "mm", cv::Point(0, sourceImg.rows - 350), cv::Scalar(0, 0, 255)); + defects.push_back("左连接片距离NG"); + if (zuoLJPtoGaiBanEnable == true) + isNg = true; + } + else//OK + textPainter.appendPaintJob("左侧连接片圆心和盖板圆心的距离:" + std::to_string(value2_1) + "mm", cv::Point(0, sourceImg.rows - 350), cv::Scalar(0, 255, 0)); + + value2_2 = halconOutput.out_CCDistance[1].D(); + if (youLJPtoGaiBanMaxThreshold < value2_2 || youLJPtoGaiBanMinThreshold > value2_2) {//NG + defects.push_back("右连接片距离NG"); + textPainter.appendPaintJob("右侧连接片圆心和盖板圆心的距离:" + std::to_string(value2_2) + "mm", cv::Point(0, sourceImg.rows - 150), cv::Scalar(0, 0, 255)); + if (youLJPtoGaiBanEnable == true) + isNg = true; + } + else + textPainter.appendPaintJob("右侧连接片圆心和盖板圆心的距离:" + std::to_string(value2_2) + "mm", cv::Point(0, sourceImg.rows - 150), cv::Scalar(0, 255, 0)); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(value2_1) + "," + QByteArray::number(value2_2)); + + data->sqlVec.insert({ "左侧连接片圆心和盖板圆心的距离", halconOutput.out_CCDistance[0].D() }); + data->sqlVec.insert({ "右侧连接片圆心和盖板圆心的距离", halconOutput.out_CCDistance[1].D() }); + } + + int left = halconOutput.out_ColorType[0].D(); + // 按左铝右铜做结果判定与叠字。 + if (left == 2) + textPainter2.appendPaintJob("铜", cv::Point(0, 1500), cv::Scalar(0, 255, 0)); + else if (left == 1) + textPainter2.appendPaintJob("铝", cv::Point(0, 1500), cv::Scalar(0, 0, 255)); + else + textPainter2.appendPaintJob("异常", cv::Point(0, 1500), cv::Scalar(0, 0, 255)); + + int right = halconOutput.out_ColorType[1].D(); + if (right == 2) + textPainter2.appendPaintJob("铜", cv::Point(Paint.cols - 500, 1500), cv::Scalar(0, 0, 255)); + else if (right == 1) + textPainter2.appendPaintJob("铝", cv::Point(Paint.cols - 500, 1500), cv::Scalar(0, 255, 0)); + else + textPainter2.appendPaintJob("异常", cv::Point(Paint.cols - 500, 1500), cv::Scalar(0, 0, 255)); + + if (left == 1 || right == 2) { + defects.push_back("极性NG"); + if (jiXingEnable) + isNg = true; + } + + + if (isNg == true) { + textPainter3.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + plcResult = "2"; + data->halconOK = false; + } + else { + textPainter3.appendPaintJob("OK", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + } + } + else { + std::string ngTails = halconOutput.out_NgTails.ToString().Text(); + cvPainter textPainter4(500); + textPainter4.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + textPainter4.appendPaintJob(gbkToUtf8(ngTails), cv::Point(0, 300), cv::Scalar(0, 0, 255)); + textPainter4.executePaintJobs(Paint); + plcResult = "2"; + data->halconOK = false; + } + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + + textPainter.executePaintJobs(Paint); + textPainter2.executePaintJobs(Paint); + textPainter3.executePaintJobs(Paint); + cvPainter textPainterx(160); + textPainterx.appendPaintJob("SN:" + data->SNcode, cv::Point(3000, 0), cv::Scalar(0, 255, 0)); + textPainterx.appendPaintJob(QDateTime::currentDateTime().toString().toStdString(), cv::Point(3000, 80), cv::Scalar(0, 255, 0)); + textPainterx.executePaintJobs(Paint); + data->insertResultImg(Paint); + //data->setNg(data->aiDetOK, data->halconOK, defects); + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "检测失败"); + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + return false; +} +halconResult_2D HanHou2D_halconEvaluate(const cv::Mat& sourceImg, const cv::Mat& segImg, cvPainter* textPainter, int times, double resolution, + double tongXingDuLimitHigh, double tongXingDuLimitLow, bool tongXingDuLimitEnable, + double neiYuanPianYiTongXinDuLimitHigh, double neiYuanPianYiTongXinDuLimitLow, bool neiYuanPianYiTongXinDuEnable, + double waiYuanPianYiTongXinDuLimitHigh, double waiYuanPianYiTongXinDuLimitLow, bool waiYuanPianYiTongXinDuEnable) { + QElapsedTimer timer; + timer.start(); + halconResult_2D result; + + rljHanHou2DInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_ImageSeg = MatToHObject(segImg); + hInput.in_FliePath = "./halconFiles/hanhou2D/"; + hInput.in_Resolution = resolution; + auto hOutput = rljHanHou2DEvaluater()->evaluate(hInput); + if (!hOutput.runFlag) { + throw std::runtime_error(hOutput.faildResult); + } + auto& halconOutput = hOutput.result; + int flag = halconOutput.out_Flag.D(); + if (flag == 1) { + + + double Distance = halconOutput.out_Distance[0].D(); + textPainter->appendPaintJob("焊印同心度:" + std::to_string(Distance), cv::Point(0, 50), cv::Scalar(0, 255, 0)); + + double angleDisplay = halconOutput.out_Angle[0].D(); + textPainter->appendPaintJob("连接片偏移角度:" + std::to_string(angleDisplay) + "°", cv::Point(0, 250), cv::Scalar(0, 255, 0)); + + result.neiYuanPianYi_X = halconOutput.out_Distance[1].D(); + result.neiYuanPianYi_Y = halconOutput.out_Distance[2].D(); + result.neiYuanTongXinDu = std::sqrtf(std::powf(halconOutput.out_Distance[1].D(), 2) + std::powf(halconOutput.out_Distance[2].D(), 2)); + + std::string x1Text = std::to_string(result.neiYuanPianYi_X); + x1Text = x1Text.substr(0, x1Text.find(".") + 4); + std::string y1Text = std::to_string(result.neiYuanPianYi_Y); + y1Text = y1Text.substr(0, y1Text.find(".") + 4); + std::string txd1Text = std::to_string(result.neiYuanTongXinDu); + txd1Text = txd1Text.substr(0, txd1Text.find(".") + 4); + if (result.neiYuanTongXinDu > neiYuanPianYiTongXinDuLimitHigh || result.neiYuanTongXinDu < neiYuanPianYiTongXinDuLimitLow) { + textPainter->appendPaintJob("内圆偏移X:" + x1Text + "mm Y:" + y1Text + "mm 同心度" + txd1Text, cv::Point(0, 2250), cv::Scalar(0, 255, 0)); + result.halconOK = false; + if (waiYuanPianYiTongXinDuEnable == true) + result.isNg = true; + } + else + textPainter->appendPaintJob("内圆偏移X:" + x1Text + "mm Y:" + y1Text + "mm 同心度" + txd1Text, cv::Point(0, 2250), cv::Scalar(0, 255, 0)); + + + result.waiYuanPianYi_X = halconOutput.out_Distance[3].D(); + result.waiYuanPianYi_Y = halconOutput.out_Distance[4].D(); + result.waiYuanTongXinDu = std::sqrtf(std::powf(halconOutput.out_Distance[3].D(), 2) + std::powf(halconOutput.out_Distance[4].D(), 2)); + + std::string x2Text = std::to_string(result.waiYuanPianYi_X); + x2Text = x2Text.substr(0, x2Text.find(".") + 4); + std::string y2Text = std::to_string(result.waiYuanPianYi_Y); + y2Text = y2Text.substr(0, y2Text.find(".") + 4); + std::string txd2Text = std::to_string(result.waiYuanTongXinDu); + txd2Text = txd2Text.substr(0, txd2Text.find(".") + 4); + if (result.waiYuanTongXinDu > waiYuanPianYiTongXinDuLimitHigh || result.waiYuanTongXinDu < waiYuanPianYiTongXinDuLimitLow) { + textPainter->appendPaintJob("外圆偏移X:" + x2Text + "mm Y:" + y2Text + "mm 同心度:" + txd2Text, cv::Point(0, 2450), cv::Scalar(0, 0, 255)); + result.halconOK = false; + if (waiYuanPianYiTongXinDuEnable == true) + result.isNg = true; + } + else + textPainter->appendPaintJob("外圆偏移X:" + x2Text + "mm Y:" + y2Text + "mm 同心度:" + txd2Text, cv::Point(0, 2450), cv::Scalar(0, 255, 0)); + + for (int i = 0; i < halconOutput.out_Circle.Length() / 3; i++) { + textPainter->appendPaintJob(cv::Point(halconOutput.out_Circle[i * 3 + 1].D(), halconOutput.out_Circle[i * 3].D()), halconOutput.out_Circle[i * 3 + 2].D(), cv::Scalar(0, 255, 0), 15, 0); + } + result.TongXingDu = Distance; + if (Distance > tongXingDuLimitHigh || Distance < tongXingDuLimitLow) { + textPainter->appendPaintJob("同心度NG", cv::Point(10, 650), cv::Scalar(0, 0, 255)); + result.halconOK = false; + if (tongXingDuLimitEnable == true) + result.isNg = true; + } + } + else { + textPainter->appendPaintJob("焊印抓取NG", cv::Point(10, 50), cv::Scalar(0, 0, 255)); + textPainter->appendPaintJob(gbkToUtf8(halconOutput.out_NgTails.ToString().Text()), cv::Point(10, 450), cv::Scalar(0, 0, 255)); + if (tongXingDuLimitEnable == true) + result.isNg = true; + result.halconOK = false; + result.ngText = gbkToUtf8(halconOutput.out_NgTails.ToString().Text()); + } + qDebug() << "焊后算法结束,耗时" << timer.elapsed(); + return result; +} +bool HanHou2D_handleAiEvaluate(std::shared_ptr data, hanHouJianCe* _detParams, const cv::Mat& sourceImg, cv::Rect hanYinRect, double resolution, cvPainter* textPainter, std::shared_ptr seg, std::shared_ptr cls) { + QElapsedTimer timer; + timer.start(); + double hanZhaFillterWidth = data->service->_paramSyncManager->get("焊后2D焊渣", "焊渣过滤值宽mm(上限)", 0.1).toDouble() / resolution; + double hanZhaFillterHeight = data->service->_paramSyncManager->get("焊后2D焊渣", "焊渣过滤值高mm(上限)", 0.1).toDouble() / resolution; + bool hanZhaEnable = data->service->_paramSyncManager->get("焊后2D焊渣", "焊渣判定启用", false).toBool(); + int dingWeiExtraSize = data->service->_paramSyncManager->get("焊后2D焊渣", "SEG定位裁切外扩大小", 0).toInt(); + int defectExtraSize = data->service->_paramSyncManager->get("焊后2D焊渣", "SEG缺陷外扩大小", 0).toInt(); + hanYinRect.x *= 4; hanYinRect.y *= 4; hanYinRect.width *= 4; hanYinRect.height *= 4; + hanYinRect.x -= dingWeiExtraSize; hanYinRect.y -= dingWeiExtraSize; hanYinRect.width += dingWeiExtraSize; hanYinRect.height += dingWeiExtraSize; + + cv::Mat forSeg; + sourceImg(hanYinRect).copyTo(forSeg); + cv::resize(forSeg, forSeg, cv::Size(), 0.5, 0.5); + data->insertSegOriginImg(forSeg); + cv::Mat SegResult = ~seg->Evaluate(forSeg); + std::vector> contours; + cv::findContours(SegResult, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + std::vector PaintRects; + std::vector>clsImgTmp; + bool isNg = false;; + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + if (rect.width > hanZhaFillterWidth || rect.height > hanZhaFillterHeight) { + rect.x -= defectExtraSize; rect.y -= defectExtraSize; rect.width += defectExtraSize; rect.height += defectExtraSize; + cv::Mat clsImg = cutImg2Cls(rect, forSeg, cv::Size(64, 64)); + cv::resize(clsImg, clsImg, cv::Size(128, 128)); + //cv::resize(forSeg(rect), clsImg, cv::Size(128, 128)); + bool clsIsNg = false; + auto clsRes = cls->Evaluate(clsImg); + if (clsRes.classes == 1) + clsIsNg = true; + if (clsIsNg == true) { + rect.x *= 2; rect.y *= 2; rect.width *= 2; rect.height *= 2; + rect.x += hanYinRect.x; rect.y += hanYinRect.y; + PaintRects.push_back(rect); + isNg = true; + } + clsImgTmp.push_back({ clsIsNg == true ? "NG" : "OK",clsImg }); + } + } + for (auto& rect : PaintRects) { + //textPainter->appendPaintJob(cv::Rect(rect.x + rect.width / 2 - 64, rect.y + rect.height / 2 - 64, 128, 128), cv::Scalar(0, 0, 255), 3); + textPainter->appendPaintJob(rect, cv::Scalar(0, 0, 255), 2); + } + data->insertClsOriginImg(clsImgTmp); + qDebug() << "焊后模型结束,耗时" << timer.elapsed(); + return isNg; +} +QByteArray getHanHouJianCe2DSendResult(bool isNg1, bool isNg2) { + if (isNg1 == false && isNg2 == false) //都OK发1 + return "1"; + else if (isNg1 == true && isNg2 == false)//第一次NG发2 + return "2"; + else if (isNg1 == false && isNg2 == true)//第二次NG发3 + return "3"; + else if (isNg1 == true && isNg2 == true)//两次都NG发4 + return "4"; + else//为了DEBUG + return "5"; +} +bool HanHouJianCe2D(std::shared_ptr data, hanHouJianCe* _detParams, sx_581_address address) { + try { + bool isDebugMode = data->service->_paramSyncManager->get("hide", "调试模式", false).toBool(); + + double cameraResolution = data->service->_paramSyncManager->get("2D相机精度", QString::fromStdString(data->cameraKeyName) + "相机精度", 0.013736).toDouble(); + double tongXingDuLimitHigh = data->service->_paramSyncManager->get("焊后2D", "焊后同心度mm(上限)", 1.8).toDouble(); + double tongXingDuLimitLow = data->service->_paramSyncManager->get("焊后2D", "焊后同心度mm(下限)", 0).toDouble(); + bool tongXingDuLimitEnable = data->service->_paramSyncManager->get("焊后2D", "焊后同心度判定启用", true).toBool(); + + double neiYuanPianYiTongXinDuLimitHigh = data->service->_paramSyncManager->get("焊后2D", "内圆偏移同心度mm(上限)", 2.0).toDouble(); + double neiYuanPianYiTongXinDuLimitLow = data->service->_paramSyncManager->get("焊后2D", "内圆偏移同心度mm(下限)", 0).toDouble(); + bool neiYuanPianYiTongXinDuEnable = data->service->_paramSyncManager->get("焊后2D", "内圆偏移同心度判定启用", false).toBool(); + + double waiYuanPianYiTongXinDuLimitHigh = data->service->_paramSyncManager->get("焊后2D", "外圆偏移同心度mm(上限)", 1.0).toDouble(); + double waiYuanPianYiTongXinDuLimitLow = data->service->_paramSyncManager->get("焊后2D", "外圆偏移同心度mm(下限)", 0).toDouble(); + bool waiYuanPianYiTongXinDuEnable = data->service->_paramSyncManager->get("焊后2D", "外圆偏移同心度判定启用", false).toBool(); + + int hanYinNgSizeWidth = data->service->_paramSyncManager->get("焊后2D", "焊印NG大小宽(pix)", 100).toInt(); + int hanYinNgSizeHeight = data->service->_paramSyncManager->get("焊后2D", "焊印NG大小高(pix)", 100).toInt(); + + bool hanYinClsTongEnable = data->service->_paramSyncManager->get("焊后2D", "焊印CLS铜判定启用", false).toBool(); + double hanYinClsTongProbThreshold = data->service->_paramSyncManager->get("焊后2D", "焊印CLS铜置信度(下限)", 0.9).toDouble(); + bool hanYinClsLvEnable = data->service->_paramSyncManager->get("焊后2D", "焊印CLS铝判定启用", true).toBool(); + double hanYinClsLvProbThreshold = data->service->_paramSyncManager->get("焊后2D", "焊印CLS铝置信度(下限)", 0.9).toDouble(); +#pragma region 错误处理 + + if (data->getOriImgSize() < 2) { + exceptionResume(data, address, "图片数量不满足"); + return true; + } + if (data->getOriImg(0).cols == 200 || (data->getOriImg(1).cols == 200)) { + data->halconOK = false; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) { + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + } + putSnAndDetTime(Paint, data->SNcode, 200); + cvPainter textPainter(40); + textPainter.appendPaintJob("图片数量不满足", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + data->insertResultImg(Paint); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + else + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_4");//结果反馈 + return true; + } +#pragma endregion + cv::Mat src1 = data->getOriImg(0); + cv::Mat src2 = data->getOriImg(1); + //src1 = cv::imread("D:\\testImage\\T.bmp",-1); + //src2 = cv::imread("D:\\testImage\\L.bmp", -1); + cv::Mat Paint1, Paint2; + src1.copyTo(Paint1); + src2.copyTo(Paint2); + if (Paint1.channels() == 1) { + cvtColor(Paint1, Paint1, cv::COLOR_GRAY2BGR); + } + if (Paint2.channels() == 1) { + cvtColor(Paint2, Paint2, cv::COLOR_GRAY2BGR); + } + std::vector defects; + QByteArray plcResult = "1"; + cv::Mat forSeg1, forSeg2; + double resizeRate = 0.25; + bool isNg1 = false, isNg2 = false; + QMutex mutex1, mutex2; + //定位铜焊印 + cv::resize(src1, forSeg1, cv::Size(), resizeRate, resizeRate); + cv::Mat seg1Ori = _detParams->seg_rljHanYinZhuaQu_T->Evaluate(forSeg1);//原始SEG 抓取区域为黑色 + cv::Mat seg1Uminus = ~seg1Ori;//取反后的SEG 抓取区域为白色 + data->insertSegOriginImg(seg1Ori); + cv::Rect tongRect = findMaxAreaContour(seg1Uminus); + if (tongRect.width < hanYinNgSizeWidth || tongRect.height < hanYinNgSizeHeight) { + isNg1 = true; + } + //定位铝焊印 + cv::resize(src2, forSeg2, cv::Size(), resizeRate, resizeRate); + cv::Mat seg2Ori = _detParams->seg_rljHanYinZhuaQu_L->Evaluate(forSeg2);//原始SEG 抓取区域为黑色 + cv::Mat seg2Uminus = ~seg2Ori;//取反后的SEG 抓取区域为白色 + data->insertSegOriginImg(seg2Ori); + cv::Rect lvRect = findMaxAreaContour(seg2Uminus); + if (lvRect.width < hanYinNgSizeWidth || lvRect.height < hanYinNgSizeHeight) { + isNg2 = true; + } + cvPainter textPainter1(200), textPainter2(200), textPainter1small(80), textPainter2small(80); + //判断焊印定位結果 + if (isNg1 == true || isNg2 == true) {//判断存图 + data->halconOK = false; + plcResult = getHanHouJianCe2DSendResult(isNg1, isNg2); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); + if (isNg1 == true) textPainter1.appendPaintJob("焊印抓取失败", cv::Point(0, 0), cv::Scalar(0, 0, 255)); + if (isNg2 == true) textPainter2.appendPaintJob("焊印抓取失败", cv::Point(0, 0), cv::Scalar(0, 0, 255)); + qDebug() << "焊印抓取失败" << QString::fromStdString(data->cameraKeyName); + cv::Mat PaintSum; + cv::vconcat(Paint1, Paint2, PaintSum); + data->insertResultImg(PaintSum); + return true; + } + //异步halcon算法 + auto futrue1 = std::async(std::launch::async, &HanHou2D_halconEvaluate, src1, seg1Ori, &textPainter1, 0, cameraResolution, tongXingDuLimitHigh, tongXingDuLimitLow, tongXingDuLimitEnable, + neiYuanPianYiTongXinDuLimitHigh, neiYuanPianYiTongXinDuLimitLow, neiYuanPianYiTongXinDuEnable, + waiYuanPianYiTongXinDuLimitHigh, waiYuanPianYiTongXinDuLimitLow, waiYuanPianYiTongXinDuEnable); + auto futrue2 = std::async(std::launch::async, &HanHou2D_halconEvaluate, src2, seg2Ori, &textPainter2, 2, cameraResolution, tongXingDuLimitHigh, tongXingDuLimitLow, tongXingDuLimitEnable, + neiYuanPianYiTongXinDuLimitHigh, neiYuanPianYiTongXinDuLimitLow, neiYuanPianYiTongXinDuEnable, + waiYuanPianYiTongXinDuLimitHigh, waiYuanPianYiTongXinDuLimitLow, waiYuanPianYiTongXinDuEnable); + //焊渣检测 + try { + HanHou2D_handleAiEvaluate(data, _detParams, src1, tongRect, cameraResolution, &textPainter1, _detParams->seg_rljHanZha, _detParams->cls_rljHanZha); + HanHou2D_handleAiEvaluate(data, _detParams, src2, lvRect, cameraResolution, &textPainter2, _detParams->seg_rljHanZha, _detParams->cls_rljHanZha); + } + catch (...) { + qDebug() << "模型检测失败!"; + } + futrue1.wait(); + futrue2.wait(); + auto halconResult1 = futrue1.get(); + isNg1 = halconResult1.isNg; + auto halconResult2 = futrue2.get(); + isNg2 = halconResult2.isNg; + + //futrue3.wait(); + //futrue4.wait(); + + //futrue3.get(); + //futrue4.get(); + + std::vector> hanYinClsImg; + + int tongClsRes = 0, lvClsRes = 0; + if (isNg1 == true || isNg2 == true) {//同心度是否NG + defects.push_back({ "焊后同心度NG" }); + } + //铜极焊印cls检测 + { + auto tongClsImg = cutImg2Cls(tongRect, forSeg1, cv::Size(128, 128)); + auto tongClsResult = _detParams->cls_hanYinTong_128->Evaluate(tongClsImg); + tongClsRes = tongClsResult.classes; + double prob = tongClsResult.prob; + hanYinClsImg.push_back({ "tongHY_Res" + std::to_string(tongClsRes) + "_prob" + std::to_string(prob),tongClsImg }); + if (tongClsRes == 1 && prob > hanYinClsTongProbThreshold) { + defects.push_back({ "铜焊印CLS-NG" }); + textPainter1.appendPaintJob("焊印CLS-NG", cv::Point(0, 850), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + if (hanYinClsTongEnable) { + isNg1 = true; + } + } + else + textPainter1.appendPaintJob("焊印CLS-OK", cv::Point(0, 850), cv::Scalar(0, 255, 0)); + textPainter1small.appendPaintJob("r" + std::to_string(tongClsRes) + "_prob" + std::to_string(prob), cv::Point(0, 1050), cv::Scalar(0, 255, 0)); + + } + //铝极焊印cls检测 + { + auto lvClsImg = cutImg2Cls(lvRect, forSeg2, cv::Size(128, 128)); + auto lvClsResult = _detParams->cls_hanYinLv_128->Evaluate(lvClsImg); + lvClsRes = lvClsResult.classes; + double prob = lvClsResult.prob; + hanYinClsImg.push_back({ "lvHY" + std::to_string(lvClsRes) + "_prob" + std::to_string(prob),lvClsImg }); + if (lvClsRes == 1 && prob > hanYinClsLvProbThreshold) { + defects.push_back({ "铝焊印CLS-NG" }); + data->aiDetOK = false; + textPainter2.appendPaintJob("焊印CLS-NG", cv::Point(0, 850), cv::Scalar(0, 0, 255)); + if (hanYinClsLvEnable) { + isNg2 = true; + } + } + else + textPainter2.appendPaintJob("焊印CLS-OK", cv::Point(0, 850), cv::Scalar(0, 255, 0)); + textPainter2small.appendPaintJob("r" + std::to_string(lvClsRes) + "_prob" + std::to_string(prob), cv::Point(0, 1050), cv::Scalar(0, 255, 0)); + } + + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(halconResult1.TongXingDu) + "," + QByteArray::number(halconResult2.TongXingDu)); + + if (isNg1 == true || isNg2 == true) {//判断存图 + data->halconOK = false; + } + + plcResult = getHanHouJianCe2DSendResult(isNg1, isNg2); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + //data->setNg(data->aiDetOK, data->halconOK, defects); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); + //发送结果结束 进行后处理 + data->insertClsOriginImg(hanYinClsImg); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + + textPainter1.executePaintJobs(Paint1); + textPainter2.executePaintJobs(Paint2); + textPainter1small.executePaintJobs(Paint1); + textPainter2small.executePaintJobs(Paint2); + //拼装结果图 + cv::Mat PaintSum; + cv::vconcat(Paint1, Paint2, PaintSum); + putSnAndDetTime(PaintSum, data->SNcode, 120); + data->insertResultImg(PaintSum); +#pragma region 处理SQL数据 + data->sqlVec.insert({ "铜焊印同心度",halconResult1.TongXingDu }); + data->sqlVec.insert({ "铜内圆同心度",halconResult1.neiYuanTongXinDu }); + data->sqlVec.insert({ "铜内圆偏移X",halconResult1.neiYuanPianYi_X }); + data->sqlVec.insert({ "铜内圆偏移Y",halconResult1.neiYuanPianYi_Y }); + data->sqlVec.insert({ "铜外圆同心度",halconResult1.waiYuanPianYi_X }); + data->sqlVec.insert({ "铜外圆偏移X",halconResult1.waiYuanPianYi_Y }); + data->sqlVec.insert({ "铜外圆偏移Y",halconResult1.waiYuanTongXinDu }); + + data->sqlVec.insert({ "铝焊印同心度",halconResult2.TongXingDu }); + data->sqlVec.insert({ "铝内圆同心度",halconResult2.neiYuanTongXinDu }); + data->sqlVec.insert({ "铝内圆偏移X",halconResult2.neiYuanPianYi_X }); + data->sqlVec.insert({ "铝内圆偏移Y",halconResult2.neiYuanPianYi_Y }); + data->sqlVec.insert({ "铝外圆同心度",halconResult2.waiYuanPianYi_X }); + data->sqlVec.insert({ "铝外圆偏移X",halconResult2.waiYuanPianYi_Y }); + data->sqlVec.insert({ "铝外圆偏移Y",halconResult2.waiYuanTongXinDu }); +#pragma endregion + data->setNg(data->aiDetOK, data->halconOK, defects); + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "检测失败"); + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + + return false; +} + +struct halconResult_3D { + bool isNg = false; + double hanPanGaoDu = -99; + cv::Point ellipseCenter = cv::Point(0, 0); + double angle = 0; + cv::Size ellipseSize = cv::Size(0, 0); + double hanPanHeight = 0; +}; +halconResult_3D HanHou3D_halconEvaluate(const cv::Mat& heightImg, const double& xResolution, const double& yResolution, const double& zResolution + , cvPainter* textPainter, const double& highThreshold, const double& lowThreshold) { + halconResult_3D result; + + rljHanHou3DInput hInput; + hInput.in_ImageH = MatToHObject(heightImg); + hInput.in_XResolution = xResolution; + hInput.in_YResolution = yResolution; + hInput.in_ZResolution = zResolution; + hInput.in_FilePath = "./halconFiles/hanhou3D/"; + auto hOutput = rljHanHou3DEvaluater()->evaluate(hInput); + if (!hOutput.runFlag) { + throw std::runtime_error(hOutput.faildResult); + } + auto& halconOutput = hOutput.result; + int flag = halconOutput.out_Flag.D(); + if (flag == 1) { + double height = halconOutput.out_Height.D(); + if (height > highThreshold || height < lowThreshold) { + result.isNg = true; + textPainter->appendPaintJob("焊盘高度:" + std::to_string(height) + "mm", cv::Point(0, 110), cv::Scalar(0, 0, 255)); + } + else + textPainter->appendPaintJob("焊盘高度:" + std::to_string(height) + "mm", cv::Point(0, 110), cv::Scalar(0, 255, 0)); + if (halconOutput.out_Ellipse.Length() == 5) { + result.ellipseCenter = cv::Point(halconOutput.out_Ellipse[0].D(), halconOutput.out_Ellipse[1].D()); + result.angle = halconOutput.out_Ellipse[2].D(); + result.ellipseSize = cv::Point(halconOutput.out_Ellipse[3].D(), halconOutput.out_Ellipse[4].D()); + } + else { + qDebug() << "无椭圆数据 size" << halconOutput.out_Ellipse.Length(); + } + } + else { + textPainter->appendPaintJob("算法NG:" + gbkToUtf8(halconOutput.out_NGDetails.ToString().Text()), cv::Point(10, 50), cv::Scalar(0, 0, 255)); + result.isNg = true; + } + + return result; +} +bool HanHouJianCe3D(std::shared_ptr data, hanHouJianCe* _detParams, sx_581_address address) { + try { + QByteArray plcResult = "1"; + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + std::vector < std::string> defects = { }; + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() < 1) { + qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); + return false; + } + if (data->getOriImg(0).rows == 100) { + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); + return false; + } + + QMutex paintMutex; + clock_t start, finish; + start = clock(); + cv::Mat src1 = data->getOriImg(0);//亮度图1 + cv::Mat height1 = data->getOriImg(1);//高度图1 + cv::Mat src2 = data->getOriImg(2);//亮度图2 + cv::Mat height2 = data->getOriImg(3);//高度图2 + cvPainter textPainter1(100), textPainter2(100); + auto highThreshold = data->service->_paramSyncManager->get("焊后3D", "焊盘高度上限(mm)", 5.0).toDouble(); + auto lowThreshold = data->service->_paramSyncManager->get("焊后3D", "焊盘高度下限(mm)", 5.0).toDouble(); + auto result1 = HanHou3D_halconEvaluate(height1, + data->service->_paramSyncManager->get("3D相机1精度", "x向精度", 0.005).toDouble(), + data->service->_paramSyncManager->get("3D相机1精度", "y向精度", 0.02).toDouble(), + data->service->_paramSyncManager->get("3D相机1精度", "z向精度", 0.0008).toDouble(), + &textPainter1, highThreshold, lowThreshold); + auto result2 = HanHou3D_halconEvaluate(height2, + data->service->_paramSyncManager->get("3D相机2精度", "x向精度", 0.005).toDouble(), + data->service->_paramSyncManager->get("3D相机2精度", "y向精度", 0.02).toDouble(), + data->service->_paramSyncManager->get("3D相机2精度", "z向精度", 0.0008).toDouble(), + &textPainter2, highThreshold, lowThreshold); + if (result1.isNg) { + plcResult = "2"; + data->halconOK = false; + } + if (result2.isNg) { + plcResult = "2"; + data->halconOK = false; + } + if (!data->service->_paramSyncManager->get("焊后3D", "焊后3D结果判定启用", false).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//发送结果 + //发送结果结束 进行后处理 + cv::Mat Paint1, Paint2, PaintSum; + src1.copyTo(Paint1); + src2.copyTo(Paint2); + if (Paint1.channels() != 3) + cv::cvtColor(Paint1, Paint1, cv::COLOR_GRAY2BGR); + if (Paint2.channels() != 3) + cv::cvtColor(Paint2, Paint2, cv::COLOR_GRAY2BGR); + + if (result1.ellipseCenter.x != 0) + cv::ellipse(Paint1, result1.ellipseCenter, result1.ellipseSize, result1.angle, 0, 360, cv::Scalar(0, 255, 0), 3); + + if (result2.ellipseCenter.x != 0) + cv::ellipse(Paint2, result2.ellipseCenter, result2.ellipseSize, result2.angle, 0, 360, cv::Scalar(0, 255, 0), 3); + textPainter1.appendPaintJob("OK", cv::Point(10, 10), cv::Scalar(0, 255, 0)); + textPainter1.executePaintJobs(Paint1); + textPainter2.appendPaintJob("OK", cv::Point(10, 10), cv::Scalar(0, 255, 0)); + textPainter2.executePaintJobs(Paint2); + cv::vconcat(Paint1, Paint2, PaintSum); + putSnAndDetTime(PaintSum, data->SNcode, 60); + + //cv::putText(Paint, "pos:" + data->additional["触发地址"].toString().toStdString(), cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + //data->service->_missionResultHandle->toBarChart(data->SNcode, !defects.size() > 0, defects);//发送图标操作 + data->insertResultImg(PaintSum); + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "检测失败"); + + return false; +} + +mission* createMission() { + return new _604_Ruanlianjie_hanqian_hanhou(); +} +void destroyMission(mission* _mission) { + delete _mission; +} diff --git a/src/mission/604/RLJ/604_Ruanlianjie_hanqian_hanhou/604_Ruanlianjie_hanqian_hanhou.h b/src/mission/604/RLJ/604_Ruanlianjie_hanqian_hanhou/604_Ruanlianjie_hanqian_hanhou.h new file mode 100644 index 0000000..caaf14c --- /dev/null +++ b/src/mission/604/RLJ/604_Ruanlianjie_hanqian_hanhou/604_Ruanlianjie_hanqian_hanhou.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +using namespace HalconCpp; + +class hanQianDingWeiParams { +public: + // 焊前定位当前没有独立可变状态,保留参数类型以匹配触发器模板。 +}; + +class hanHouJianCe { +public: + // 2D 焊后检测复用的 Seg/Cls 模型句柄。 + std::shared_ptr seg_rljHanYinZhuaQu_T = nullptr; + std::shared_ptr seg_rljHanYinZhuaQu_L = nullptr; + std::shared_ptr seg_rljHanZha = nullptr; + std::shared_ptr cls_rljHanZha = nullptr; + std::shared_ptr cls_hanYinTong_128 = nullptr; + std::shared_ptr cls_hanYinLv_128 = nullptr; + int ngCnt = 0; +}; + +bool HanQianDingWei(std::shared_ptr data, hanQianDingWeiParams* _detParams, sx_581_address address); +bool HanHouJianCe2D(std::shared_ptr data, hanHouJianCe* _detParams, sx_581_address address); +bool HanHouJianCe3D(std::shared_ptr data, hanHouJianCe* _detParams, sx_581_address address); + +class _604_Ruanlianjie_hanqian_hanhou : public mission { +public: + // 焊前两相机定位,焊后两工位 2D/3D 联检。 + _581_SX_Single_triggerTemplate dingwei_1; + _581_SX_Single_triggerTemplate dingwei_2; + + _581_SX_Single_triggerTemplate jiance_2d_1; + _581_SX_Single_triggerTemplate jiance_2d_2; + _581_SX_Single_triggerTemplate jiance_3d_1; + _581_SX_Single_triggerTemplate jiance_3d_2; + + hanQianDingWeiParams hanqiandingweiparams; + std::vectorhanhoujiance2dparams = std::vector(2); + hanHouJianCe hanhoujiance3dparams; + + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + + if (initOnce == false) { + hanhoujiance2dparams[0].seg_rljHanYinZhuaQu_T = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_rljHanYinZhuaQu_T.BHD", 16, 0, "RLJ焊后铜焊印抓取Seg网"); + hanhoujiance2dparams[0].seg_rljHanYinZhuaQu_L = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_rljHanYinZhuaQu_L.BHD", 16, 0, "RLJ焊后铝焊印抓取Seg网"); + hanhoujiance2dparams[1].seg_rljHanYinZhuaQu_T = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_rljHanYinZhuaQu_T.BHD", 16, 1, "RLJ焊后铜焊印抓取Seg网"); + hanhoujiance2dparams[1].seg_rljHanYinZhuaQu_L = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_rljHanYinZhuaQu_L.BHD", 16, 1, "RLJ焊后铝焊印抓取Seg网"); + + hanhoujiance2dparams[0].seg_rljHanZha = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_rljHanZha.BHD", 16, 0, "RLJ焊渣Seg网"); + hanhoujiance2dparams[1].seg_rljHanZha = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_rljHanZha.BHD", 16, 1, "RLJ焊渣Seg网"); + + hanhoujiance2dparams[0].cls_rljHanZha = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_rljHanZha.BHD", 1, 0, "RLJ焊渣Cls网"); + hanhoujiance2dparams[1].cls_rljHanZha = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_rljHanZha.BHD", 1, 1, "RLJ焊渣Cls网"); + + hanhoujiance2dparams[0].cls_hanYinTong_128 = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_hanYinTong_128.BHD", 1, 0, "RLJ铜焊印Cls网"); + hanhoujiance2dparams[1].cls_hanYinTong_128 = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_hanYinTong_128.BHD", 1, 1, "RLJ铜焊印Cls网"); + + hanhoujiance2dparams[0].cls_hanYinLv_128 = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_hanYinLv_128.BHD", 1, 0, "RLJ铝焊印Cls网"); + hanhoujiance2dparams[1].cls_hanYinLv_128 = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_hanYinLv_128.BHD", 1, 1, "RLJ铝焊印Cls网"); + initOnce = true; + } + } + bool initCustom() { +#pragma region 注册触发器回调 + dingwei_1.init("Camera1", "焊前定位-相机1(左)", { { 4900, 1,{4910},"4902","4904",4930} }, 1, &hanqiandingweiparams, _serviceManager, bridge, HanQianDingWei,"焊前定位CCD"); + dingwei_2.init("Camera2", "焊前定位-相机2(右)", { { 5800, 1,{5810},"5802","5804",5830} }, 1, &hanqiandingweiparams, _serviceManager, bridge, HanQianDingWei,"焊前定位CCD"); + + jiance_2d_1.init("Camera3", "焊后检测2D-相机1(前)", { { 5400, 1,{5410},"5402","5404",5430} }, 2, &hanhoujiance2dparams[0], _serviceManager, bridge, HanHouJianCe2D, "焊后检测CCD"); + jiance_3d_1.init("Camera4", "焊后检测3D-相机1(前)", { { 5500, 1,{5510},"5502","5504",5530} }, 4, &hanhoujiance3dparams, _serviceManager, bridge, HanHouJianCe3D,"焊后检测CCD"); + jiance_2d_2.init("Camera5", "焊后检测2D-相机2(后)", { { 5600, 1,{5610},"5602","5604",5630} }, 2, &hanhoujiance2dparams[1], _serviceManager, bridge, HanHouJianCe2D, "焊后检测CCD"); + jiance_3d_2.init("Camera6", "焊后检测3D-相机2(后)", { { 5700, 1,{5710},"5702","5704",5730} }, 4, &hanhoujiance3dparams, _serviceManager, bridge, HanHouJianCe3D,"焊后检测CCD"); +#pragma endregion + return true; + } +}; \ No newline at end of file diff --git a/src/mission/581_YuHan_hanqian/CMakeLists.txt b/src/mission/604/RLJ/604_Ruanlianjie_hanqian_hanhou/CMakeLists.txt similarity index 100% rename from src/mission/581_YuHan_hanqian/CMakeLists.txt rename to src/mission/604/RLJ/604_Ruanlianjie_hanqian_hanhou/CMakeLists.txt diff --git a/src/mission/604/RLJ/CMakeLists.txt b/src/mission/604/RLJ/CMakeLists.txt new file mode 100644 index 0000000..f9a95fd --- /dev/null +++ b/src/mission/604/RLJ/CMakeLists.txt @@ -0,0 +1,11 @@ +# CMakeList.txt: 遍历文件夹 添加子项目 + +set(OUR_LIBS "") +SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF) +message("OUR_LIBS :" ${OUR_LIBS}) + + +# 遍历子库 +foreach(dll ${OUR_LIBS}) + add_subdirectory(${dll}) +endforeach(dll ${}) diff --git a/src/mission/604/baoMo/604-BaoMo-Cam1213/604-BaoMo-Cam1213.cpp b/src/mission/604/baoMo/604-BaoMo-Cam1213/604-BaoMo-Cam1213.cpp new file mode 100644 index 0000000..1780511 --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam1213/604-BaoMo-Cam1213.cpp @@ -0,0 +1,271 @@ +#include <604-BaoMo-Cam1213.h> + +struct baoMoDituoInput { + HObject in_Image; + HTuple in_Par; + HTuple in_FilePath; + HTuple in_Resolution; +}; + +struct baoMoDituoOutput { + std::vector out_VectorX; + std::vector out_VectorY; + HTuple out_Flag; + HTuple out_NgDetails; +}; + +static std::shared_ptr>& baoMoDituoEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_4_TD_BlueCover_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const baoMoDituoInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Par"] = in.in_Par; + inputHTuple["in_FilePath"] = in.in_FilePath; + inputHTuple["in_Resolution"] = in.in_Resolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + baoMoDituoOutput& out) { + out.out_VectorX = outputHTupleVector["out_VectorX"]; + out.out_VectorY = outputHTupleVector["out_VectorY"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + }); + return instance; + }(); + return evaluater; +} + +struct halconResult { + bool isNg = false; + double resultX = -99; + double resultY = -99; + std::vector < std::string> defects; +}; + +void exceptionResume(std::shared_ptr data, yh_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob(why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("强制OK", false).toBool()) + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(-99));//发送结果 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(-99));//发送结果 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_" + QByteArray::number(-99));//发送结果 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 6) + "_" + QByteArray::number(-99));//发送结果 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1,0,1");//发送结果 + +} +halconResult dituo(std::shared_ptr data, cv::Mat& src, cv::Mat& Paint, bool isLeft, const std::shared_ptr& cls) { + halconResult result; + try { + if (src.channels() != 3) + cv::cvtColor(src, Paint, cv::COLOR_GRAY2BGR); + else + src.copyTo(Paint); + + float resolution = data->service->_paramSyncManager->get("相机精度", QString::fromStdString(data->cameraKeyName) + "精度", 0.046276).toFloat(); + HTuple in_Par, in_FilePath("./halconFiles/"); + double widthThreshold = data->service->_paramSyncManager->get("底托检测", "宽度阈值(mm)", 2.0).toDouble(); + double heightThreshold = data->service->_paramSyncManager->get("底托检测", "宽度阈值(mm)", 2.0).toDouble(); + double areaThreshold = data->service->_paramSyncManager->get("底托检测", "宽度阈值(mm)", 2.0).toDouble(); + bool clsJugmentEnable = data->service->_paramSyncManager->get("底托检测", "CLS判定启用", true).toBool(); + double clsProbThreshold = data->service->_paramSyncManager->get("底托检测", "CLS置信度阈值", 0.9).toDouble(); + in_Par[0] = widthThreshold; + in_Par[1] = heightThreshold; + in_Par[2] = areaThreshold; + baoMoDituoInput hInput; + hInput.in_Image = MatToHObject(src); + hInput.in_Par = in_Par; + hInput.in_FilePath = in_FilePath; + hInput.in_Resolution = resolution; + auto hOutput = baoMoDituoEvaluater()->evaluate(hInput); + if (!hOutput.runFlag) { + throw std::runtime_error(hOutput.faildResult); + } + auto& halconOutput = hOutput.result; + //HTuple out_RecX = procedureCall.GetOutputCtrlParamTuple("out_RecX"); + //HTuple out_RecY = procedureCall.GetOutputCtrlParamTuple("out_RecY"); + int outFlag = halconOutput.out_Flag.D(); + std::vector> clsImgs; + cvPainter textPainterdefect(30); + if (outFlag == 0) { + cv::Mat whiteMat = cv::Mat::zeros(cv::Size(src.cols, src.rows), CV_8UC1); + whiteMat.setTo(cv::Scalar(0)); + std::vector> contours; + int l1 = static_cast(halconOutput.out_VectorX.size()), l2 = static_cast(halconOutput.out_VectorY.size()), l3 = halconOutput.out_NgDetails.Length(); + for (int m = 0; m < halconOutput.out_VectorX.size() && m < halconOutput.out_VectorY.size() && m < halconOutput.out_NgDetails.Length(); m++) { + HalconCpp::HTuple out_X = halconOutput.out_VectorX[m]; + HalconCpp::HTuple out_Y = halconOutput.out_VectorY[m]; + std::string ngDetails = (halconOutput.out_NgDetails[m].ToTuple().ToString().Text()); + + //输出的点集不是正常的轮廓点集,把点集在黑图上画出来,在用opencv重新找一遍轮廓 + std::vector contour; + int lengthX = out_X.Length(); + int lengthY= out_Y.Length(); + for (int i = 0; i < lengthX && i < lengthY && lengthX == lengthY; i++) { + contour.push_back(cv::Point(out_X[i].D(), out_Y[i].D())); + //whiteMat.at(out_Y[i].D(), out_X[i].D()) = (255); + cv::circle(whiteMat, cv::Point(out_X[i].D(), out_Y[i].D()), 1, cv::Scalar(255), 1); + } + contours.push_back(contour); + } + for (int j = 0; j < contours.size(); j++) { + std::string ngDetails = (halconOutput.out_NgDetails[j].ToTuple().ToString().Text()); + ngDetails = gbkToUtf8(ngDetails.substr(1, ngDetails.length() - 2)); + if (data->service->_paramSyncManager->get("底托检测", "底托偏移检测启用", false).toBool() == false && ngDetails == "底托偏移") + return result; + cv::Rect rect = cv::boundingRect(contours[j]); + rect = rect & cv::Rect(0, 0, src.cols, src.rows); + if (rect.width * resolution >= widthThreshold || rect.height * resolution >= heightThreshold) { + float realWidth = rect.width; + float realHeight = rect.height; + float realArea = cv::contourArea(contours[j]); + cv::Mat clsImg = src(rect); + cv::resize(clsImg, clsImg, cv::Size(128, 128)); + auto clsResult = cls->Evaluate(clsImg); + double prob = clsResult.prob; + clsImgs.push_back({ "result" + std::to_string(clsResult.classes) + "_prob" + std::to_string(prob),clsImg }); + if (clsJugmentEnable == true) {//CLS处理 + if (clsResult.classes != 0 && prob > clsProbThreshold) {//NG + data->halconOK = false; + result.defects.push_back(ngDetails); + result.isNg = true; + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + } + else + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 3); + QString DefectStr = "宽:" + QString::number(realWidth * resolution, 'f', 3) + + "mm高:" + QString::number(realHeight * resolution, 'f', 3) + + "mm面积:" + QString::number(realArea * resolution * resolution, 'f', 3) + + "r" + QString::number(clsResult.classes) + "_p" + QString::number(prob); + textPainterdefect.appendPaintJob(DefectStr.toStdString() + ngDetails, cv::Point(rect.x, rect.y), cv::Scalar(0, 255, 255)); + } + else {//不经过CLS直接判为NG + data->halconOK = false; + result.defects.push_back(ngDetails); + result.isNg = true; + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + QString DefectStr = "宽:" + QString::number(realWidth * resolution, 'f', 3) + + "mm高:" + QString::number(realHeight * resolution, 'f', 3) + + "mm面积:" + QString::number(realArea * resolution * resolution, 'f', 3); + textPainterdefect.appendPaintJob(DefectStr.toStdString() + ngDetails, cv::Point(rect.x, rect.y), cv::Scalar(0, 255, 255)); + } + } + } + //画轮廓 + std::vector> contoursPaint; + cv::findContours(whiteMat, contoursPaint, cv::RETR_TREE, cv::CHAIN_APPROX_NONE); + int maxIndex = 0, maxArea = 0; + for (int i = 0; i < contoursPaint.size(); i++) { + cv::Rect rect = cv::boundingRect(contoursPaint[i]); + if (rect.width * resolution >= widthThreshold || rect.height * resolution >= heightThreshold) { + cv::drawContours(Paint, contoursPaint, i, cv::Scalar(0, 0, 255), 3); + } + } + //释放存储画图文字 + textPainterdefect.executePaintJobs(Paint); + } + data->insertClsOriginImg(clsImgs); + return result; + } + CATCH_EXCEPTIONS; + return result; +} +bool ALGO_BaoMoHouDiBu(std::shared_ptr data, baoMoHouDiBuDetParams* _detParams, yh_581_address address) { + try { + QDateTime createTime = QDateTime::currentDateTime(); + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + std::vector < std::string> defects; + + //data->oriImgs.clear(); + + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() < 2) + { + qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); + exceptionResume(data, address, "图片数量不满足要求"); + return false; + } + if (data->getOriImgVec()[0].rows == 100) { + exceptionResume(data, address, "取图超时"); + return false; + } + + cv::Mat Paint, Paint1, Paint2; + bool leftFirst = address.triggerAddr != 20200; + cv::Mat first = data->getOriImg(0); + cv::Mat second = data->getOriImg(1); + if (true == data->service->_paramSyncManager->get("hide", "离线测试", false).toBool()) { + first = cv::imread("D:/testImages/left.bmp", -1); + second = cv::imread("D:/testImages/right.bmp", -1); + data->service->_paramSyncManager->edit("hide", "离线测试", false); + } + auto feature1 = std::async(std::launch::async, dituo, data, std::ref(first), std::ref(Paint1), leftFirst, _detParams->cls1); + auto feature2 = std::async(std::launch::async, dituo, data, std::ref(second), std::ref(Paint2), ~leftFirst, _detParams->cls2); + + feature1.wait(); + halconResult hResult1 = feature1.get(); + defects.insert(defects.end(), hResult1.defects.begin(), hResult1.defects.end()); + + feature2.wait(); + halconResult hResult2 = feature2.get(); + defects.insert(defects.end(), hResult2.defects.begin(), hResult2.defects.end()); + + if (address.triggerAddr == 20400) + cv::hconcat(Paint2, Paint1, Paint); + else + cv::hconcat(Paint1, Paint2, Paint); + + bool isNG = (hResult1.isNg == true || hResult2.isNg == true); + cvPainter textPainter(150); + + QByteArray plcResult = "1"; + if (isNG == false) { + textPainter.appendPaintJob("OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + else { + textPainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + if (data->service->_paramSyncManager->get("底托检测", "底托判定启用", false).toBool()) + plcResult = "2"; + } + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20; + cvPainter textPainter1(Rate); + textPainter1.appendPaintJob("产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainter1.appendPaintJob("获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + textPainter.executePaintJobs(Paint); + textPainter1.executePaintJobs(Paint); + + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(hResult1.resultX));//发送结果 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(hResult1.resultY));//发送结果 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_" + QByteArray::number(hResult2.resultX));//发送结果 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 6) + "_" + QByteArray::number(hResult2.resultY));//发送结果 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult + ",0," + plcResult);//发送结果 + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + data->insertResultImg(Paint); + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "检测异常"); + return true; +} + +mission* createMission() { + return new _604_BaoMo(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/baoMo/604-BaoMo-Cam1213/604-BaoMo-Cam1213.h b/src/mission/604/baoMo/604-BaoMo-Cam1213/604-BaoMo-Cam1213.h new file mode 100644 index 0000000..3a7d6a6 --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam1213/604-BaoMo-Cam1213.h @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +QMutex writeFileMutex; +QStringList headList({ "条码", "时间","位置","结果","缺陷明细" }); + +class baoMoHouDiBuDetParams { +public: + std::shared_ptr cls1 = nullptr; + std::shared_ptr cls2 = nullptr; +}; +static bool ALGO_BaoMoHouDiBu(std::shared_ptr data, baoMoHouDiBuDetParams* _detParams, yh_581_address address); + +class _604_BaoMo : public mission { +public: + using baoMoHouDiBuTrigger = _581_Single_triggerTemplate; + baoMoHouDiBuTrigger _station_1; + baoMoHouDiBuTrigger _station_2; + + baoMoHouDiBuDetParams _baoMoHouDiBuDetParams1; + baoMoHouDiBuDetParams _baoMoHouDiBuDetParams2; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + _baoMoHouDiBuDetParams1.cls1 = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/dituo_cls.BHD", 1, 0, "包膜后底托左侧Cls网1"); + _baoMoHouDiBuDetParams1.cls2 = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/dituo_cls.BHD", 1, 0, "包膜后底托左侧Cls网2"); + _baoMoHouDiBuDetParams2.cls1 = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/dituo_cls.BHD", 1, 0, "包膜后底托右侧Cls网1"); + _baoMoHouDiBuDetParams2.cls2 = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/dituo_cls.BHD", 1, 0, "包膜后底托右侧Cls网2"); + } + bool initCustom() { +#pragma region 注册触发器回调 + + _station_1.init("Camera1","包膜后底托位置度检测-左-相机12",20200,1,2, &_baoMoHouDiBuDetParams1,_serviceManager,bridge, ALGO_BaoMoHouDiBu); + _station_2.init("Camera2", "包膜后底托位置度检测-右-相机13",20400,1,2,&_baoMoHouDiBuDetParams2, _serviceManager, bridge, ALGO_BaoMoHouDiBu); +#pragma endregion + return true; + } + + + //9600 +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/baoMo/604-BaoMo-Cam1213/CMakeLists.txt b/src/mission/604/baoMo/604-BaoMo-Cam1213/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam1213/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/baoMo/604-BaoMo-Cam1234/604-BaoMo-Cam1234.cpp b/src/mission/604/baoMo/604-BaoMo-Cam1234/604-BaoMo-Cam1234.cpp new file mode 100644 index 0000000..cc54e42 --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam1234/604-BaoMo-Cam1234.cpp @@ -0,0 +1,684 @@ +#include <604-BaoMo-Cam1234.h> + +//切Cls小图,返回单张 +cv::Mat cutLabelImg(cv::Mat pic, int W, int H, cv::Rect rect, int edgeType, int largeType, int color) +{ + cv::Point center; + center.x = rect.x + rect.width / 2; + center.y = rect.y + rect.height / 2; + + if (rect.width <= W && rect.height <= H) + { + if (edgeType == 0) + { + cv::Rect rec = { center.x - W / 2,center.y - H / 2,W,H }; + if (rec.x < 0) + rec.x = 0; + if (rec.y < 0) + rec.y = 0; + if (rec.x + rec.width > pic.cols) + rec.x = pic.cols - rec.width; + if (rec.y + rec.height > pic.rows) + rec.y = pic.rows - rec.height; + + cv::Mat picCut; + pic(rec).copyTo(picCut); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + + int x, y; + cv::Rect rec; + x = center.x - W / 2; + y = center.y - H / 2; + rec.x = x; + rec.y = y; + rec.width = W; + rec.height = H; + cv::Mat picCut(H, W, pic.type(), cv::Scalar(color, color, color)); + if (x < 0) + { + rec.width += rec.x; + xDiff = 0 - rec.x; + rec.x = 0; + } + if (y < 0) + { + rec.height += rec.y; + yDiff = 0 - rec.y; + rec.y = 0; + } + if (rec.x + rec.width > pic.cols) + rec.width = pic.cols - rec.x; + if (rec.y + rec.height > pic.rows) + rec.height = pic.rows - rec.y; + + pic(rec).copyTo(picCut({ xDiff, yDiff, rec.width, rec.height })); + return picCut; + } + } + else + { + if (largeType == 0) + { + int xDiff = 0; + int yDiff = 0; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rect.x < 0) + { + rect.width += rect.x; + rect.x = 0; + } + if (rect.y < 0) + { + rect.height += rect.y; + rect.y = 0; + } + if (rect.x + rect.width > pic.cols) + rect.width = pic.cols - rect.x; + if (rect.y + rect.height > pic.rows) + rect.height = pic.rows - rect.y; + + xDiff = (wTem - rect.width) * 1.0 / 2; + yDiff = (hTem - rect.height) * 1.0 / 2; + + pic(rect).copyTo(picCut({ xDiff, yDiff, rect.width, rect.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + cv::Rect rectTem; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + rectTem = { rect.x + rect.width / 2 - wTem / 2,rect.y + rect.height / 2 - hTem / 2,wTem,hTem }; + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rectTem.x < 0) + { + rectTem.width += rectTem.x; + xDiff = 0 - rectTem.x; + rectTem.x = 0; + } + if (rectTem.y < 0) + { + rectTem.height += rectTem.y; + yDiff = 0 - rectTem.y; + rectTem.y = 0; + } + if (rectTem.x + rectTem.width > pic.cols) + rectTem.width = pic.cols - rectTem.x; + if (rectTem.y + rectTem.height > pic.rows) + rectTem.height = pic.rows - rectTem.y; + + pic(rectTem).copyTo(picCut({ xDiff, yDiff, rectTem.width, rectTem.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + } + + return cv::Mat(); +} + +void exceptionResume(std::shared_ptr data, yh_581_address address, QString why = "", QString location = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("强制OK", false).toBool()) + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + QStringList resultCsv; + resultCsv << QString::fromStdString(data->SNcode) << QDateTime::currentDateTime().toString("yyyyMMddhhmmss") << location << why; +} + +cv::Rect extractRegion(cv::Mat& src, int binary_threshold = 50) { + cv::Mat binary; + threshold(src, binary, binary_threshold, 255, cv::THRESH_BINARY); + cv::dilate(binary, binary, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5))); + std::vector> contours; + findContours(binary, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + + double maxArea = 0; + int maxAreaIdx = -1; + for (size_t i = 0; i < contours.size(); i++) { + double area = contourArea(contours[i]); + if (area > maxArea) { + maxArea = area; + maxAreaIdx = i; + } + } + cv::Rect boundingRect = cv::boundingRect(contours[maxAreaIdx]); + return boundingRect; +} +int index = 0; +bool BaoMoQiangDaMian(std::shared_ptr data, baoMoQianDaMianDetParams* _detParams, yh_581_address address) { + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + ///////////////////取图//处理////////////////// + try { + QDateTime createTime = QDateTime::currentDateTime(); + QByteArray plcResult = "1"; + QString objectName = data->cameraKeyName == "Camera1" ? "上大面" : "下大面"; + double resizeX = data->service->_paramSyncManager->get(objectName, "X方向缩放倍率", 0.43).toDouble(); + double resizeY = data->service->_paramSyncManager->get(objectName, "Y方向缩放倍率", 0.405).toDouble(); + int extraSize = data->service->_paramSyncManager->get(objectName, "定位后外扩大小(pix)", 75).toInt(); + double in_xResolution = data->service->_paramSyncManager->get(objectName, "相机x方向精度", 1.0).toDouble(); + double in_yResolution = data->service->_paramSyncManager->get(objectName, "相机y方向精度", 1.0).toDouble(); + double MinWidth = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值宽(mm)", 60.0).toDouble(); + double MinHeight = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值高(mm)", 60.0).toDouble(); + double MinArea = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值面积(mm²)", 60.0).toDouble(); + double ignoreWidth = data->service->_paramSyncManager->get(objectName, "Seg不检测阈值宽(mm)", 0.5).toDouble(); + double ignoreHeight = data->service->_paramSyncManager->get(objectName, "Seg不检测阈值高(mm)", 0.5).toDouble(); + double clsProbThreshold = data->service->_paramSyncManager->get(objectName, "Cls置信度阈值下限", 0.9).toDouble(); + int textOffset = data->service->_paramSyncManager->get(objectName, "缺陷打印位置Y方向偏移", 10).toInt(); + cv::Mat src = data->getOriImg(0); + //src = cv::imread("D:\\readSnEmpty09-53-06-55_Camera1_ORI_0_09-53-07-892.bmp"); + //////////////////////检测////////////////////// + cv::Mat afterDingWei; + { + cv::Mat forDingWei; + cv::resize(src, forDingWei, cv::Size(), 0.1, 0.1); + cv::Mat segResult = ~_detParams->dingwei_segVec.at(address.index)->Evaluate(forDingWei); + auto rect = findMaxAreaContour(segResult); + rect.x *= 10; rect.y *= 10; rect.width *= 10; rect.height *= 10; + if (rect.width < 4000 || rect.height < 4000) { + exceptionResume(data, address, "定位失败", objectName); + return true; + } + //往外扩50pix + + rect.x -= extraSize; rect.y -= extraSize; rect.width += extraSize * 2; rect.height += extraSize * 2; + src(rect & cv::Rect(0, 0, src.cols, src.rows)).copyTo(afterDingWei); + } + + cv::resize(afterDingWei, afterDingWei, cv::Size(), resizeX, resizeY); + cv::Mat Paint; + if (afterDingWei.channels() == 1) + cv::cvtColor(afterDingWei, Paint, cv::COLOR_GRAY2RGB); + else + afterDingWei.copyTo(Paint); + + data->insertSegOriginImg(afterDingWei); + cv::Mat segResult = ~_detParams->baomodamian_segVec.at(address.index)->Evaluate(afterDingWei); + data->insertSegOriginImg(segResult); + //data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1"); + std::vector>contours; + cv::findContours(segResult, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + bool isNg = false; + TextRendererZH textPainter(Paint.cols, Paint.rows, 500); + + + std::vector> clsImgs; + TextRendererZH textPainter1(Paint.cols, Paint.rows, 30); + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + float realArea = cv::contourArea(contours[i]) * in_yResolution * in_xResolution / resizeX / resizeY; + float realHeight = rect.height * in_yResolution / resizeY; + float realWidth = rect.width * in_xResolution / resizeX; + QString sizeStr = "宽:" + QString::number(realWidth, 'f', 3) + "mm高:" + + QString::number(realHeight, 'f', 3) + "mm面积:" + QString::number(realArea, 'f', 3) + "mm²"; + if (realWidth < ignoreWidth || realHeight < ignoreHeight) + continue; + if (realWidth < MinWidth || realHeight < MinHeight) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, "尺寸过滤" + sizeStr.toStdString(), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + continue; + } + if (realArea < MinArea) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, "面积过滤" + sizeStr.toStdString(), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + continue; + } + bool clsIsNg = false; + cv::Mat clsImg = afterDingWei(rect); + cv::resize(clsImg, clsImg, cv::Size(128, 128)); + auto clsResult = _detParams->baomodamian_clsVec.at(address.index)->Evaluate(clsImg); + double prob = clsResult.prob; + std::string defectName; + if (clsResult.classes != 0 && prob > clsProbThreshold) { + defectName = "破损"; + clsIsNg = true; + } + else + defectName = "OK"; + //switch (clsResult) { + //case 0: + // defectName = "OK"; + // break; + //case 1: + // defectName = "破损"; + // clsIsNg = true; + // break; + //case 2: + // defectName = "脏污"; + // break; + //case 3: + // defectName = "褶皱"; + // break; + //case 4: + // defectName = "贴胶"; + // break; + //case 5: + // defectName = "其他"; + // break; + //default: + // defectName = "未知缺陷"; + // break; + //} + //std::vector clsImgs = cutImg2ClsVec(rect, afterDingWei); + //bool clsIsNg = false; + //for (auto& clsImg : clsImgs) { + // int clsResult = _detParams->baomodamian_clsVec.at(batteryOffset)->Evaluate(clsImg); + // if (clsResult != 0) + // clsIsNg = true; + //} + + if (clsIsNg == true) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 0, 255), 3); + textPainter1.putText(Paint, defectName + sizeStr.toStdString() + "_r" + std::to_string(clsResult.classes) + "_p" + std::to_string(prob), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 0, 255)); + isNg = true; + } + else { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, defectName + sizeStr.toStdString() + "_r" + std::to_string(clsResult.classes) + "_p" + std::to_string(prob), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + } + + clsImgs.push_back({ "result" + std::to_string(clsResult.classes) + "_prob" + std::to_string(prob) , clsImg }); + } + data->insertClsOriginImg(clsImgs); + //data->service->_missionResultHandle->addSqlData(QString::fromStdString(data->SNcode), { {"包膜上大面结果", isNg } }); + if (isNg == true) { + textPainter.putText(Paint, "NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + } + else + { + textPainter.putText(Paint, "OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20; + TextRendererZH textPainterx(Paint.cols, Paint.rows, Rate); + textPainterx.putText(Paint, "产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainterx.putText(Paint, "获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + data->insertResultImg(Paint); + + if (isNg) + { + data->service->_missionResultHandle->toBarChart(data->SNcode, { objectName.toStdString() + "NG" }); + plcResult = "2"; + } + else + data->service->_missionResultHandle->toBarChart(data->SNcode, {}); + if (data->service->_paramSyncManager->get(objectName, "判定启用", false).toBool() == false) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "检测异常", "大面"); + return true; + +} + +bool BaoMoQiangDaMian_Test(std::shared_ptr data, baoMoQianDaMianDetParams* _detParams, yh_581_address address) { + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + ///////////////////取图//处理////////////////// + try { + QDateTime createTime = QDateTime::currentDateTime(); + QByteArray plcResult = "1"; + QString objectName = data->cameraKeyName == "Camera1" ? "上大面" : "下大面"; + double resizeX = data->service->_paramSyncManager->get(objectName, "X方向缩放倍率", 0.43).toDouble(); + double resizeY = data->service->_paramSyncManager->get(objectName, "Y方向缩放倍率", 0.405).toDouble(); + int extraSize = data->service->_paramSyncManager->get(objectName, "定位后外扩大小(pix)", 75).toInt(); + double in_xResolution = data->service->_paramSyncManager->get(objectName, "相机x方向精度", 1.0).toDouble(); + double in_yResolution = data->service->_paramSyncManager->get(objectName, "相机y方向精度", 1.0).toDouble(); + double MinWidth = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值宽(mm)", 60.0).toDouble(); + double MinHeight = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值高(mm)", 60.0).toDouble(); + double MinArea = data->service->_paramSyncManager->get(objectName, "Seg筛选阈值面积(mm²)", 60.0).toDouble(); + double ignoreWidth = data->service->_paramSyncManager->get(objectName, "Seg不检测阈值宽(mm)", 0.5).toDouble(); + double ignoreHeight = data->service->_paramSyncManager->get(objectName, "Seg不检测阈值高(mm)", 0.5).toDouble(); + double clsProbThreshold = data->service->_paramSyncManager->get(objectName, "Cls置信度阈值下限", 0.9).toDouble(); + int textOffset = data->service->_paramSyncManager->get(objectName, "缺陷打印位置Y方向偏移", 10).toInt(); + cv::Mat src = data->getOriImg(0); + //src = cv::imread("D:\\readSnEmpty09-53-06-55_Camera1_ORI_0_09-53-07-892.bmp"); + //////////////////////检测////////////////////// + + //src = cv::imread("E:\\testImages\\Bottom.bmp", -1); + + float resizeRate = 10; + cv::Mat resizeImage; + cv::resize(src, resizeImage, cv::Size(), 1 / resizeRate, 1 / resizeRate, cv::INTER_AREA); + cv::Mat binaryImage1 = ~_detParams->dingwei_segVec.at(address.index)->Evaluate(resizeImage);//seg模型调用,获取产品位置的二值图轮廓 + cv::Rect cutRect = findMaxAreaContour(binaryImage1);//找最大的轮廓,注意:输入的二值图为黑色背景,白色缺陷轮廓(黑色中找白色) + //把找到的轮廓位置还原到原图 + { + cutRect.x = cutRect.x * resizeRate - 75; + cutRect.y = cutRect.y * resizeRate - 75; + cutRect.width = cutRect.width * resizeRate + 150; + cutRect.height = cutRect.height * resizeRate + 150; + } + + cutRect = cutRect & cv::Rect(0, 0, src.cols, src.rows);//防止切图越界 + + //检测图 + cv::Mat afterDingWei; + src(cutRect).copyTo(afterDingWei); + float resizeRateX = 2800.0 / afterDingWei.cols; + float resizeRateY = 2600.0 / afterDingWei.rows; + cv::resize(afterDingWei, afterDingWei, cv::Size(), resizeRateX, resizeRateY, cv::INTER_AREA); + cv::Mat Paint; + if (afterDingWei.channels() == 1) + cv::cvtColor(afterDingWei, Paint, cv::COLOR_GRAY2RGB); + else + afterDingWei.copyTo(Paint); + + data->insertSegOriginImg(afterDingWei); + cv::Mat binaryImageDefect = ~_detParams->baomodamian_segVec.at(address.index)->Evaluate(afterDingWei); + data->insertSegOriginImg(binaryImageDefect); + + //对二值图进行处理,屏蔽掉无效区域,减少过检 + //暂时不启用,等待定位模型优化一版后启用,启用时注意下面findContours输入矩阵!!! + cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15));//设置内核 + cv::Mat openMat; + cv::morphologyEx(binaryImage1, openMat, cv::MORPH_OPEN, element);//对定位模型输出的二值图进行开运算(先膨胀后腐蚀),消除小毛刺,在纤细的地方分离 + cv::Mat erMat; + element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)); + cv::erode(openMat, erMat, element);//腐蚀,缩小二值图中白色区域 + cv::resize(erMat, erMat, cv::Size(), resizeRate, resizeRate, cv::INTER_CUBIC);//将二值图还原到原图大小 + erMat = erMat(cutRect);//裁切掉无效区域 + cv::resize(erMat, erMat, cv::Size(), resizeRateX, resizeRateY, cv::INTER_AREA);//二值图缩放到缺陷检测图大小 + cv::Mat mask = erMat; + cv::Mat SegResult; + binaryImageDefect.copyTo(SegResult, erMat);//屏蔽无效区域,cpoyTo会将InputArray中不为零的区域输出到OutputArray中,为0的区域会在OutputArray相应区域输出0,相当于“与”的操作 + + std::vector>contours; + cv::findContours(SegResult, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + //cv::findContours(binaryImageDefect, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + bool isNg = false; + TextRendererZH textPainter(Paint.cols, Paint.rows, 500); + + std::vector> clsImgs; + TextRendererZH textPainter1(Paint.cols, Paint.rows, 30); + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + float realArea = cv::contourArea(contours[i]) * in_yResolution * in_xResolution / resizeRateX / resizeRateY; + float realHeight = rect.height * in_yResolution / resizeRateY; + float realWidth = rect.width * in_xResolution / resizeRateX; + QString sizeStr = "宽:" + QString::number(realWidth, 'f', 3) + "mm高:" + + QString::number(realHeight, 'f', 3) + "mm面积:" + QString::number(realArea, 'f', 3) + "mm²"; + if (realWidth < ignoreWidth || realHeight < ignoreHeight) + continue; + if (realWidth < MinWidth || realHeight < MinHeight) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, "尺寸过滤" + sizeStr.toStdString(), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + continue; + } + if (realArea < MinArea) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, "面积过滤" + sizeStr.toStdString(), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + continue; + } + bool clsIsNg = false; + cv::Mat clsImg = cutLabelImg(afterDingWei, 128, 128, rect, 1, 1, 0); + auto clsResult = _detParams->baomodamian_clsVec.at(address.index)->Evaluate(clsImg); + double prob = clsResult.prob; + std::string defectName; + switch (clsResult.classes) { + case 0: { + defectName = "OK"; + break; + } + case 1: { + if (prob > clsProbThreshold) { + cv::Mat mask = binaryImageDefect(rect); + cv::Scalar grayValue = cv::mean(afterDingWei(rect), mask); + if (grayValue[0] < 175)//平均灰度过滤,减少过检 + clsIsNg = true; + } + defectName = "破损"; + break; + } + case 2: { + defectName = "脏污"; + break; + } + case 3: { + defectName = "褶皱"; + break; + } + case 4: { + defectName = "贴胶"; + break; + } + case 5: { + defectName = "其他"; + break; + } + default: { + defectName = "未知缺陷"; + break; + } + } + + if (clsIsNg == true) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 0, 255), 3); + textPainter1.putText(Paint, defectName + sizeStr.toStdString() + "_r" + std::to_string(clsResult.classes) + "_p" + std::to_string(prob), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 0, 255)); + isNg = true; + } + else { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter1.putText(Paint, defectName + sizeStr.toStdString() + "_r" + std::to_string(clsResult.classes) + "_p" + std::to_string(prob), { rect.x + rect.width / 2,rect.y + rect.height / 2 + textOffset }, cv::Scalar(0, 255, 0)); + } + + clsImgs.push_back({ "result" + std::to_string(clsResult.classes) + "_prob" + std::to_string(prob) , clsImg }); + } + data->insertClsOriginImg(clsImgs); + //data->service->_missionResultHandle->addSqlData(QString::fromStdString(data->SNcode), { {"包膜上大面结果", isNg } }); + if (isNg == true) { + textPainter.putText(Paint, "NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + } + else { + textPainter.putText(Paint, "OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20; + TextRendererZH textPainterx(Paint.cols, Paint.rows, Rate); + textPainterx.putText(Paint, "产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainterx.putText(Paint, "获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + data->insertResultImg(Paint); + + if (isNg) { + data->service->_missionResultHandle->toBarChart(data->SNcode, { objectName.toStdString() + "NG" }); + plcResult = "2"; + } + else + data->service->_missionResultHandle->toBarChart(data->SNcode, {}); + if (data->service->_paramSyncManager->get(objectName, "判定启用", false).toBool() == false) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); + + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "检测异常", "大面"); + + return true; +} + +bool BaoMoQianXiaDaMianSiJiao(std::shared_ptr data, baoMoQianDaMianSiJiaoDetParams* _detParams, yh_581_address address) { + + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + index++; + //batteryOffset = batteryOffset > _detParams->baoMoQianDaMianSiJiao_segVec.size() - 1 ? _detParams->baoMoQianDaMianSiJiao_segVec.size() - 1 : batteryOffset; + + ///////////////////取图//处理////////////////// + try { + int batteryOffset = QString::number(address.triggerAddr).endsWith('0') ? 0 : 1; + QDateTime createTime = QDateTime::currentDateTime(); + QByteArray plcResult = "1"; + //data->oriImgs.clear(); + //data->oriImgs.push_back(cv::imread("D:\\testImg\\sijiao-1.bmp", -1)); + //data->oriImgs.push_back(cv::imread("D:\\testImg\\sijiao-2.bmp", -1)); + std::vectorresultImgs; + bool isNg = false; + for (auto& src : data->getOriImgVec()) { + + //////////////////////检测////////////////////// + cv::Mat resizeImg; + cv::resize(src, resizeImg, cv::Size(), 0.5, 0.5); + cv::Mat Paint; + if (resizeImg.channels() == 1) + cv::cvtColor(resizeImg, Paint, cv::COLOR_GRAY2RGB); + else + resizeImg.copyTo(Paint); + data->insertSegOriginImg(resizeImg); + cv::Mat segResult = ~_detParams->baoMoQianDaMianSiJiao_segVec.at(batteryOffset)->Evaluate(resizeImg); + //data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1"); + std::vector>contours; + cv::findContours(segResult, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + double MinWidth = data->service->_paramSyncManager->get("下大面边缘", "Seg筛选阈值宽(mm)", 20.0).toDouble(); + double MinHeight = data->service->_paramSyncManager->get("下大面边缘", "Seg筛选阈值高(mm)", 20.0).toDouble(); + double MinArea = data->service->_paramSyncManager->get("下大面边缘", "Seg筛选阈值面积(mm²)", 10.0).toDouble(); + float in_xResolution_in = data->service->_paramSyncManager->get("下大面边缘内侧", "相机x方向精度", 0.041792).toFloat(); + float in_yResolution_in = data->service->_paramSyncManager->get("下大面边缘内侧", "相机y方向精度", 0.0418278).toFloat(); + float in_xResolution_out = data->service->_paramSyncManager->get("下大面边缘外侧", "相机x方向精度", 0.0418404).toFloat(); + float in_yResolution_out = data->service->_paramSyncManager->get("下大面边缘外侧", "相机y方向精度", 0.0419411).toFloat(); + float in_yResolution, in_xResolution; + if (data->cameraKeyName == "Camera3") { + in_xResolution = in_xResolution_out; + in_yResolution = in_yResolution_out; + } + if (data->cameraKeyName == "Camera4") { + in_xResolution = in_xResolution_in; + in_yResolution = in_yResolution_in; + } + TextRendererZH textPainter(Paint.cols, Paint.rows, 500); + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + float realArea = cv::contourArea(contours[i]) * in_yResolution * in_xResolution; + float realHeight = rect.height * in_yResolution; + float realWidth = rect.width * in_xResolution; + if (realWidth < MinWidth || realHeight < MinHeight) + continue; + if (realArea < MinArea) + continue; + std::vector clsImgs = cutImg2ClsVec(rect, resizeImg); + bool clsIsNg = false; + for (auto& clsImg : clsImgs) { + auto clsResult = _detParams->baoMoQianDaMianSiJiao_clsVec.at(batteryOffset)->Evaluate(clsImg); + if (clsResult.classes == 1) + /*clsIsNg = true*/; + } + TextRendererZH textPainter1(Paint.cols, Paint.rows, 30); + QString drawStr = "缺陷宽:" + QString::number(realWidth, 'f', 3) + "(mm)缺陷高:" + + QString::number(realHeight, 'f', 3) + "(mm)缺陷面积:" + QString::number(realArea, 'f', 3) + "(mm²)"; + /*if (clsIsNg == true) { + + cv::drawContours(Paint, contours, i, cv::Scalar(0, 0, 255), 2); + textPainter1.putText(Paint, drawStr.toStdString(), { rect.x + rect.width / 2,rect.y + rect.height / 2 }, cv::Scalar(0, 0, 255)); + isNg = true; + defctList << drawStr; + } + else*/ + { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 255, 0), 2); + textPainter1.putText(Paint, drawStr.toStdString(), { rect.x + rect.width / 2,rect.y + rect.height / 2 }, cv::Scalar(0, 255, 0)); + } + for (auto& clsImg : clsImgs) + data->insertClsOriginImg({ std::make_pair(clsIsNg == true ? "NG" : "OK",clsImg) }); + } + //cv::rotate(Paint, Paint, cv::ROTATE_90_CLOCKWISE); + resultImgs.push_back(Paint); + } + + cv::Mat Paint; + if (resultImgs.size() == 2) + cv::hconcat(resultImgs[0], resultImgs[1], Paint); + else + resultImgs[0].copyTo(Paint); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 200); + + if (isNg == true) { + textPainter.putText(Paint, "NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + } + else + { + textPainter.putText(Paint, "OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20; + TextRendererZH textPainter1(Paint.cols, Paint.rows, Rate); + textPainter1.putText(Paint, "产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainter1.putText(Paint, "获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + data->insertResultImg(Paint); + + //data->service->_missionResultHandle->addSqlData(QString::fromStdString(data->SNcode), { {"包膜下大面边缘结果", isNg } }); + + if (isNg) + { + data->service->_missionResultHandle->toBarChart(data->SNcode, { "下大面四角NG" }); + plcResult = "2"; + } + else + data->service->_missionResultHandle->toBarChart(data->SNcode, {}); + if (data->service->_paramSyncManager->get("下大面边缘", "判定启用", false).toBool() == false) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); + + return true; + } + CATCH_EXCEPTIONS + exceptionResume(data, address, "检测异常", "下大面四角"); + return true; + +} +mission* createMission() { + return new _581_BaoMo_Cam1234(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/baoMo/604-BaoMo-Cam1234/604-BaoMo-Cam1234.h b/src/mission/604/baoMo/604-BaoMo-Cam1234/604-BaoMo-Cam1234.h new file mode 100644 index 0000000..d2e04f9 --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam1234/604-BaoMo-Cam1234.h @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +QMutex writeFileMutex; +QStringList headList({ "条码", "时间","位置","结果","缺陷明细"}); +class baoMoQianDaMianDetParams { +public: + std::vector> dingwei_segVec;// = nullptr; + std::vector> baomodamian_segVec;// = nullptr; + std::vector> baomodamian_clsVec;// = nullptr; +}; +class baoMoQianDaMianSiJiaoDetParams { +public: + std::vector> baoMoQianDaMianSiJiao_segVec;// = nullptr; + std::vector> baoMoQianDaMianSiJiao_clsVec;// = nullptr; +}; + + +bool BaoMoQiangDaMian(std::shared_ptr data, baoMoQianDaMianDetParams* _detParams, yh_581_address address); +bool BaoMoQiangDaMian_Test(std::shared_ptr data, baoMoQianDaMianDetParams* _detParams, yh_581_address address); +bool BaoMoQianXiaDaMianSiJiao(std::shared_ptr data, baoMoQianDaMianSiJiaoDetParams* _detParams, yh_581_address address); +class _581_BaoMo_Cam1234 : public mission { +public: + using baoMoQianDaMianTrigger = _581_Union_triggerTemplate; + baoMoQianDaMianTrigger _shangdamian_1; + baoMoQianDaMianTrigger _xiadamian_2; + std::vector _baoMoQianDaMianDetParams = std::vector< baoMoQianDaMianDetParams>(2); + + _581_Union_triggerTemplate _station_3; + _581_Union_triggerTemplate _station_4; + baoMoQianDaMianSiJiaoDetParams _baoMoQianDaMianSiJiaoDetParams1; + baoMoQianDaMianSiJiaoDetParams _baoMoQianDaMianSiJiaoDetParams2; + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_602.hdpl", bridge); + + if (initOnce == false) { + for (int i = 0; i < 2; i++) + { + _baoMoQianDaMianDetParams[0].dingwei_segVec.push_back(_serviceManager->_handleAiManager->loadSegModel("./handleAiModels/BaoMoQianDaMianDingWei_Seg.BHD", 16, 0, "包膜前大面定位Seg网0")); + _baoMoQianDaMianDetParams[1].dingwei_segVec.push_back(_serviceManager->_handleAiManager->loadSegModel("./handleAiModels/BaoMoQianDaMianDingWei_Seg.BHD", 16, 0, "包膜前大面定位Seg网0")); + + /*_baoMoQianDaMianDetParams[0].baomodamian_segVec.push_back(handdleAI::CreateSegObjS("./handleAiModels/DaMian_Seg_GPU0.BHD", 16, 0)); + _baoMoQianDaMianDetParams[1].baomodamian_segVec.push_back(handdleAI::CreateSegObjS("./handleAiModels/DaMian_Seg_GPU1.BHD", 16, 1)); + + _baoMoQianDaMianDetParams[0].baomodamian_clsVec.push_back(handdleAI::CreateClsObjV3("./handleAiModels/DaMian_Cls_GPU0.BHD", 0, 1)); + _baoMoQianDaMianDetParams[1].baomodamian_clsVec.push_back(handdleAI::CreateClsObjV3("./handleAiModels/DaMian_Cls_GPU1.BHD", 1, 1));*/ + + _baoMoQianDaMianDetParams[0].baomodamian_segVec.push_back(_serviceManager->_handleAiManager->loadSegModel("./handleAiModels/DaMianTest/DaMian_Seg_GPU0.BHD", 16, 0, "包膜前大面Seg网0")); + _baoMoQianDaMianDetParams[1].baomodamian_segVec.push_back(_serviceManager->_handleAiManager->loadSegModel("./handleAiModels/DaMianTest/DaMian_Seg_GPU1.BHD", 16, 0, "包膜前大面Seg网1")); + + _baoMoQianDaMianDetParams[0].baomodamian_clsVec.push_back(_serviceManager->_handleAiManager->loadClsModel("./handleAiModels/DaMianTest/DaMian_Cls_GPU0.BHD", 1, 0, "包膜前大面Cls网0")); + _baoMoQianDaMianDetParams[1].baomodamian_clsVec.push_back(_serviceManager->_handleAiManager->loadClsModel("./handleAiModels/DaMianTest/DaMian_Cls_GPU1.BHD", 1, 0, "包膜前大面Cls网1")); + + _baoMoQianDaMianSiJiaoDetParams1.baoMoQianDaMianSiJiao_segVec.push_back(_serviceManager->_handleAiManager->loadSegModel("./handleAiModels/siJiao_seg.BHD", 16, 0, "包膜前四角Seg网1")); + _baoMoQianDaMianSiJiaoDetParams1.baoMoQianDaMianSiJiao_clsVec.push_back(_serviceManager->_handleAiManager->loadClsModel("./handleAiModels/siJiao_cls.BHD", 1, 0, "包膜前四角Cls网1")); + + _baoMoQianDaMianSiJiaoDetParams2.baoMoQianDaMianSiJiao_segVec.push_back(_serviceManager->_handleAiManager->loadSegModel("./handleAiModels/siJiao_seg.BHD", 16, 0, "包膜前四角Seg网2")); + _baoMoQianDaMianSiJiaoDetParams2.baoMoQianDaMianSiJiao_clsVec.push_back(_serviceManager->_handleAiManager->loadClsModel("./handleAiModels/siJiao_cls.BHD", 1, 0, "包膜前四角Cls网2")); + } + + initOnce = true; + } + } + bool initCustom() { +#pragma region 注册触发器回调 + //_shangdamian_1.init("Camera1","包膜前外观检测大面相机1(上大面)", 18000,2, 1, &_baoMoQianDaMianDetParams[0], _serviceManager, bridge, BaoMoQiangDaMian); + _shangdamian_1.init("Camera1","包膜前外观检测大面相机1(上大面)", 18000,2, 1, &_baoMoQianDaMianDetParams[0], _serviceManager, bridge, BaoMoQiangDaMian_Test); + + //int tmp = _shangdamian_1.address[0].snAddr; + //_shangdamian_1.address[0].snAddr = _shangdamian_1.address[1].snAddr; + //_shangdamian_1.address[1].snAddr = tmp; + + //QByteArray Resaddress = _shangdamian_1.address[0].resultAddr; + //_shangdamian_1.address[0].resultAddr = _shangdamian_1.address[1].resultAddr; + //_shangdamian_1.address[1].resultAddr = Resaddress; + //_xiadamian_2.init("Camera2", "包膜前外观检测大面相机2(下大面)", 18200,2, 1, &_baoMoQianDaMianDetParams[1], _serviceManager, bridge, BaoMoQiangDaMian); + _xiadamian_2.init("Camera2", "包膜前外观检测大面相机2(下大面)", 18200,2, 1, &_baoMoQianDaMianDetParams[1], _serviceManager, bridge, BaoMoQiangDaMian_Test); + // tmp = _xiadamian_2.address[0].snAddr; + //_xiadamian_2.address[0].snAddr = _xiadamian_2.address[1].snAddr; + //_xiadamian_2.address[1].snAddr = tmp; + + // Resaddress = _xiadamian_2.address[0].resultAddr; + //_xiadamian_2.address[0].resultAddr = _xiadamian_2.address[1].resultAddr; + //_xiadamian_2.address[1].resultAddr = Resaddress; + _station_3.init("Camera3", "包膜前产品边缘检测大面-外-相机3(下大面顶盖侧)", 18400,2, 2, &_baoMoQianDaMianSiJiaoDetParams1,_serviceManager,bridge, BaoMoQianXiaDaMianSiJiao); + + // tmp = _station_3.address[0].snAddr; + //_station_3.address[0].snAddr = _station_3.address[1].snAddr; + //_station_3.address[1].snAddr = tmp; + + // Resaddress = _station_3.address[0].resultAddr; + //_station_3.address[0].resultAddr = _station_3.address[1].resultAddr; + //_station_3.address[1].resultAddr = Resaddress; + + _station_4.init("Camera4", "包膜前产品边缘检测大面-内-相机4(下大面底部侧)", 18600 ,2, 2, &_baoMoQianDaMianSiJiaoDetParams2, _serviceManager, bridge, BaoMoQianXiaDaMianSiJiao); + + // tmp = _station_4.address[0].snAddr; + //_station_4.address[0].snAddr = _station_4.address[1].snAddr; + //_station_4.address[1].snAddr = tmp; + + // Resaddress = _station_4.address[0].resultAddr; + //_station_4.address[0].resultAddr = _station_4.address[1].resultAddr; + //_station_4.address[1].resultAddr = Resaddress; +#pragma endregion + return true; + } + void reloadAlgo(QByteArray& byteArray) { + + } + + //9600 +}; + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/baoMo/604-BaoMo-Cam1234/CMakeLists.txt b/src/mission/604/baoMo/604-BaoMo-Cam1234/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam1234/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/baoMo/604-BaoMo-Cam5678/604-BaoMo.cpp b/src/mission/604/baoMo/604-BaoMo-Cam5678/604-BaoMo.cpp new file mode 100644 index 0000000..4d5f61c --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam5678/604-BaoMo.cpp @@ -0,0 +1,8 @@ +#include <604-BaoMo.h> + +mission* createMission() { + return new _581_BaoMo(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/baoMo/604-BaoMo-Cam5678/604-BaoMo.h b/src/mission/604/baoMo/604-BaoMo-Cam5678/604-BaoMo.h new file mode 100644 index 0000000..35390df --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam5678/604-BaoMo.h @@ -0,0 +1,680 @@ +#include +#include +#include +struct _581_BaoMoResult +{ + bool result = true; + std::vector < std::string> defects; +}; +class baoMoHouDaMianDetParams { +public: + std::shared_ptr dingwei_seg = nullptr; + std::shared_ptr seg = nullptr; + std::shared_ptr cls = nullptr; +}; +#pragma region 算法 +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +#pragma region 算法 +struct baoMoCoverInspectInput { + HObject in_Image; + HTuple in_Par; + HTuple in_xResolution; + HTuple in_yResolution; +}; + +struct baoMoCoverInspectOutput { + std::vector out_VectorRows; + std::vector out_VectorCols; + HTuple out_NgDetails; + HTuple out_Flag; +}; + +struct baoMoLongCoatingInput { + HObject in_Image; + HTuple in_xResolution; + HTuple in_yResolution; +}; + +struct baoMoLongCoatingOutput { + HTuple out_Distance; + HTuple out_Line; + HTuple out_Flag; +}; + +static std::shared_ptr>& baoMoCoverInspectEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_4_Inspection_Cover", + [](std::map& inputHObj, + std::map& inputHTuple, + const baoMoCoverInspectInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Par"] = in.in_Par; + inputHTuple["in_xResolution"] = in.in_xResolution; + inputHTuple["in_yResolution"] = in.in_yResolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + baoMoCoverInspectOutput& out) { + out.out_VectorRows = outputHTupleVector["out_VectorRows"]; + out.out_VectorCols = outputHTupleVector["out_VectorCols"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& baoMoLongCoatingEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_4_Cal_Long_Coatingcover_Dist", + [](std::map& inputHObj, + std::map& inputHTuple, + const baoMoLongCoatingInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_xResolution"] = in.in_xResolution; + inputHTuple["in_yResolution"] = in.in_yResolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + baoMoLongCoatingOutput& out) { + out.out_Distance = outputHTuple["out_Distance"]; + out.out_Line = outputHTuple["out_Line"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + return instance; + }(); + return evaluater; +} + +void exceptionResume(std::shared_ptr data, baoMoHouDaMianDetParams* _detParams, yh_581_address address, QString why = "", QString location = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob(why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + + //if (data->service->_paramSyncManager->get("强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 +} +std::vector BaoMoHou_DaMianAi(std::shared_ptr data, cv::Mat src, cv::Mat& Paint, + baoMoHouDaMianDetParams* _detParams, bool isLeft, cvPainter* textPainter, cvPainter* textPainterLarge) { + QElapsedTimer timer; + timer.start(); + if (_detParams->seg == nullptr || _detParams->cls == nullptr) + return {}; + //bool isLeft = address.triggerAddr == 19200 ? true : false; + std::vector defects; + try { + float in_xResolution; + float in_yResolution; + if (isLeft) + { + in_xResolution = data->service->_paramSyncManager->get("上大面", "左侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("上大面", "左侧相机y方向精度", 0.029527).toDouble(); + } + else + { + in_xResolution = data->service->_paramSyncManager->get("上大面", "右侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("上大面", "右侧相机y方向精度", 0.029527).toDouble(); + } + + float MinWidth = data->service->_paramSyncManager->get("上大面", "Seg筛选阈值宽(mm)", 100).toFloat(); + float MinHeight = data->service->_paramSyncManager->get("上大面", "Seg筛选阈值高(mm)", 100).toFloat(); + float MinArea = data->service->_paramSyncManager->get("上大面", "Seg筛选阈值面积(mm²)", 400).toFloat(); + + bool huaShangNg = false; + cv::Mat segImg = ~_detParams->seg->Evaluate(src); + std::vector> contours; + cv::findContours(segImg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + bool isOK = true; + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + float realArea = cv::contourArea(contours[i]) * in_yResolution * in_xResolution; + float realHeight = rect.height * in_yResolution; + float realWidth = rect.width * in_xResolution; + if (realWidth < MinWidth || realHeight < MinHeight) continue; // 过滤掉小轮廓 + if (realArea < MinArea)continue; + auto clsImgs = cutImg2ClsVec(rect, src, cv::Size(128, 128)); + bool clsIsNg = false; + for (auto& clsImg : clsImgs) { + auto clsResult = _detParams->cls->Evaluate(clsImg); + if (clsResult.classes == 1) { + clsIsNg = true; + huaShangNg = true; + data->aiDetOK = false; + } + data->insertClsOriginImg({ std::make_pair("CLS",clsImg) }); + } + QString defectStr = "缺陷宽:" + QString::number(realWidth, 'f', 3) + "mm高:" + QString::number(realHeight, 'f', 3) + "mm面积:" + QString::number(realArea, 'f', 3) + "mm²"; + + if (clsIsNg == true) { + cv::Scalar color = cv::Scalar(0, 0, 255); + isOK = false; + //textPainter->appendPaintJob(contours[i], color, 2); + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(rect.x + rect.width / 2, rect.y + rect.height / 2), cv::Scalar(0, 0, 255)); + //cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + //textPainter.putText(Paint, "NG", cv::Point(rect.x + 10, rect.y - 20), color); + } + else + { + //textPainter->appendPaintJob(contours[i], cv::Scalar(0, 255, 0), 2); + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(rect.x + rect.width / 2, rect.y + rect.height / 2), cv::Scalar(0, 255, 0)); + } + } + if (huaShangNg == true) + defects.push_back("划伤"); + qDebug() << "上大面耗时:" << timer.elapsed() << "ms"; + return defects; + } + CATCH_EXCEPTIONS + defects.push_back("AI检测流程异常"); + textPainterLarge->appendPaintJob("AI检测流程异常", cv::Point(300, 50), cv::Scalar(0, 0, 255)); + return defects; +} + +struct halconResult { +public: + bool isNg = false; + std::vector < std::string> ngTexts; + std::vector> drawPoints; +}; +halconResult BaoMoHou_poSun(HalconCpp::HObject& inImage, std::shared_ptr data, yh_581_address address,float& in_xResolution, float& in_yResolution,float resizeRate) { + auto houduAlgoStart = std::chrono::high_resolution_clock::now();//包膜后厚度检测算法开始时间点 + halconResult hResult; + bool isOK = false; + double widthThreshold = data->service->_paramSyncManager->get("包膜后上大面", "最小宽度缺陷阈值(mm)", 3.0).toFloat(); + double heightThreshold = data->service->_paramSyncManager->get("包膜后上大面", "最小高度缺陷阈值(mm)", 3.0).toFloat(); + //HalconCpp::HObject in_Image = MatToHObject(src); + HalconCpp::HObject in_Image = inImage; + auto houduConvertImageEnd = std::chrono::high_resolution_clock::now();//Mat转Halcon结束的时间点 + HTuple in_Par; + in_Par.Append(widthThreshold); + in_Par.Append(heightThreshold); + baoMoCoverInspectInput hInput; + hInput.in_Image = in_Image; + hInput.in_Par = in_Par; + hInput.in_xResolution = HTuple(in_xResolution); + hInput.in_yResolution = HTuple(in_yResolution); + auto hOutput = baoMoCoverInspectEvaluater()->evaluate(hInput); + int outFlag = hOutput.runFlag ? hOutput.result.out_Flag.D() : 1; + auto houduHalconExecuteEnd = std::chrono::high_resolution_clock::now();//hanlcon算法执行完成的时间点 + if (outFlag == 0) { + auto& halconOutput = hOutput.result; + int out_VectorRowsLength = static_cast(halconOutput.out_VectorRows.size()); + int out_VectorColsLength = static_cast(halconOutput.out_VectorCols.size()); + int out_NgDetailsLength = halconOutput.out_NgDetails.Length(); + qDebug() << "out_VectorRowsLength" << out_VectorRowsLength << " out_VectorColsLength" << out_VectorColsLength << " out_NgDetailsLength" << out_NgDetailsLength; + hResult.isNg = true; + for (int i = 0; i < out_VectorRowsLength && i < out_VectorColsLength && i < out_NgDetailsLength; i++) { + HTuple rows = halconOutput.out_VectorRows[i]; + HTuple cols = halconOutput.out_VectorCols[i]; + std::vector points; + for (int o = 0; o < rows.Length() && o < cols.Length(); o++) { + points.push_back(cv::Point( cols[o].D() * resizeRate, rows[o].D() * resizeRate)); + } + if (points.size() == 0)continue; + hResult.drawPoints.push_back(points); + hResult.ngTexts.push_back(gbkToUtf8(halconOutput.out_NgDetails[i].ToTuple().ToString().Text())); + } + + } + //else { + // //data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(Width));//算法失败发送-99 + // //textPainter->appendPaintJob(gbkToUtf8(out_NgDetails.ToString().Text()), cv::Point(50, 200), cv::Scalar(0, 0, 255)); + // data->halconOK = false; + // hResult.ngText = gbkToUtf8(out_NgDetails.ToString().Text()); + //} + + auto houduAlgoEnd = std::chrono::high_resolution_clock::now();//包膜后厚度检测完成的时间点 + +#pragma region 打印耗时 + auto detectTotalTime = std::chrono::duration_cast(houduAlgoEnd - houduAlgoStart); + auto convertImageTime = std::chrono::duration_cast(houduConvertImageEnd - houduAlgoStart); + auto halconExecuteTime = std::chrono::duration_cast(houduHalconExecuteEnd - houduConvertImageEnd); + auto paintResultTime = std::chrono::duration_cast(houduAlgoEnd - houduHalconExecuteEnd); + + qDebug() << __FUNCTION__ << " line:" << __LINE__ + << ", SN" << QString::fromStdString(data->SNcode) + << "包膜破损检测总耗时:" << detectTotalTime.count() << "ms,\n" + << "包膜破损检测Mat转Halcon耗时:" << convertImageTime.count() << "ms,\n" + << "包膜破损检测Halcon执行耗时:" << halconExecuteTime.count() << "ms,\n" + << "包膜破损检测结果绘制耗时:" << paintResultTime.count() << "ms"; +#pragma endregion + + return hResult; +} +bool BaoMoHou_ShangDaMian(std::shared_ptr data, baoMoHouDaMianDetParams* _detParams, yh_581_address address){ + try { + QDateTime createTime = QDateTime::currentDateTime(); + //data->oriImgs[0] = cv::imread("D://testImage//Camera1.bmp", -1); + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + QByteArray PlcResult = "1"; + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() != 1){ + qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); + return false; + } + /*if (data->oriImgs[0].rows == 150) { + cv::Mat Paint; + data->oriImgs[0].copyTo(Paint); + cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); + return false; + }*/ + bool isLeft = address.triggerAddr == 19200 ? true : false; + cv::Mat Paint; + //QMutex paintMutex; + clock_t start, finish; + start = clock(); + + cv::Mat src1 = data->getOriImg(0); + cv::Mat srcHalf; + cv::resize(src1, srcHalf, cv::Size(0, 0), 0.5, 0.5); + if (srcHalf.channels() != 3) + cv::cvtColor(srcHalf, Paint, cv::COLOR_GRAY2BGR); + else + srcHalf.copyTo(Paint); + cvPainter textPainter(150); + cvPainter textPainterTiny(20); + float in_xResolution; + float in_yResolution; + if (isLeft) + { + in_xResolution = data->service->_paramSyncManager->get("上大面", "左侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("上大面", "左侧相机y方向精度", 0.029527).toDouble(); + } + else + { + in_xResolution = data->service->_paramSyncManager->get("上大面", "右侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("上大面", "右侧相机y方向精度", 0.029527).toDouble(); + } + + //std::future > handleAiFuture = std::async(std::launch::async, BaoMoHou_DaMianAi, data, srcHalf, Paint, _detParams, isLeft,&textPainterTiny,&textPainter); + HalconCpp::HObject in_Image = MatToHObject(src1); + //auto futrue = std::async(std::launch::async, BaoMoHou_poSun, std::ref(in_Image), data, address, std::ref(in_xResolution), std::ref(in_yResolution), 0.5); + baoMoLongCoatingInput hInput; + hInput.in_Image = in_Image; + hInput.in_xResolution = HTuple(in_xResolution); + hInput.in_yResolution = HTuple(in_yResolution); + auto hOutput = baoMoLongCoatingEvaluater()->evaluate(hInput); + int outFlag = hOutput.runFlag ? hOutput.result.out_Flag.D() : 0; + double batteryWidth = -99, batteryLength = -99, UpmoDaoDingGai = -99, MidmoDaoDingGai = -99, DowmmoDaoDingGai = -99; + + double distanceUp = data->service->_paramSyncManager->get("上大面", "上端盖到膜的距离(mm)", 10.0).toDouble(); + double distanceDown = data->service->_paramSyncManager->get("上大面", "下端盖到膜的距离(mm)", 130).toDouble(); + double distanceMid = data->service->_paramSyncManager->get("上大面", "中端盖到膜的距离(mm)", 80.0).toDouble(); + double distanceWidthMax = data->service->_paramSyncManager->get("上大面", "电芯宽度阈值最大值(mm)", 200.0).toDouble(); + double distanceWidthMin = data->service->_paramSyncManager->get("上大面", "电芯宽度阈值最小值(mm)", 0.0).toDouble(); + double distanceLengthMax = data->service->_paramSyncManager->get("上大面", "电芯长度阈值最大值(mm)", 200.0).toDouble(); + double distanceLengthMin = data->service->_paramSyncManager->get("上大面", "电芯长度阈值最小值(mm)", 0.0).toDouble(); + + //handleAiFuture.wait(); + //auto defects = handleAiFuture.get(); + std::vectordefects; + if (outFlag == 1){ + auto& halconOutput = hOutput.result; + //out_Distance[0]: 电芯宽度 < / c> + // * out_Distance[1] : 电芯高度< / c> + // * out_Distance[2] : 上端盖到膜的距离< / c> + // * out_Distance[3] : 中端盖到膜的距离< / c> + // * out_Distance[4] : 下端盖到膜的距离 < / c> + batteryLength = halconOutput.out_Distance[0].D();//客户用长宽而不是宽高 + batteryWidth = halconOutput.out_Distance[1].D(); + UpmoDaoDingGai = halconOutput.out_Distance[2].D(); + MidmoDaoDingGai = halconOutput.out_Distance[3].D(); + DowmmoDaoDingGai = halconOutput.out_Distance[4].D(); + + for (int i = 0; i < halconOutput.out_Line.Length() / 4 - 1; i++){ + textPainter.appendPaintJob(cv::Point(halconOutput.out_Line[i * 4 + 1].D(), halconOutput.out_Line[i * 4].D()) / 2, + cv::Point(halconOutput.out_Line[i * 4 + 3].D(), halconOutput.out_Line[i * 4 + 2].D()) / 2, + cv::Scalar(0, 255, 0), 5); + } + bool lengthEnable = data->service->_paramSyncManager->get("上大面", "长度判定启用", false).toBool(); + if (batteryLength > distanceLengthMax || batteryLength < distanceLengthMin){ + textPainter.appendPaintJob("电芯长度:" + std::to_string(batteryLength) + "mm", cv::Point(50, 150), cv::Scalar(0, 0, 255)); + if(lengthEnable) + data->halconOK = false; + } + else{ + textPainter.appendPaintJob("电芯长度:" + std::to_string(batteryLength) + "mm", cv::Point(50, 150), cv::Scalar(0, 255, 0)); + } + bool widthEnable = data->service->_paramSyncManager->get("上大面", "宽度判定启用", false).toBool(); + if (batteryWidth > distanceWidthMax || batteryWidth < distanceWidthMin){ + textPainter.appendPaintJob("电芯宽度:" + std::to_string(batteryWidth) + "mm", cv::Point(50, 350), cv::Scalar(0, 0, 255)); + if (widthEnable) + data->halconOK = false; + } + else{ + textPainter.appendPaintJob("电芯宽度:" + std::to_string(batteryWidth) + "mm", cv::Point(50, 350), cv::Scalar(0, 255, 0)); + } + bool moDaoDingGaiEnable = data->service->_paramSyncManager->get("上大面", "膜到顶盖距离判定启用", false).toBool(); + if (UpmoDaoDingGai > distanceUp ){ + textPainter.appendPaintJob("上端盖到膜的距离:" + std::to_string(halconOutput.out_Distance[2].D()) + "mm", cv::Point(50, 550), cv::Scalar(0, 0, 255)); + if(moDaoDingGaiEnable) + data->halconOK = false; + } + else{ + textPainter.appendPaintJob("上端盖到膜的距离:" + std::to_string(halconOutput.out_Distance[2].D()) + "mm", cv::Point(50, 550), cv::Scalar(0, 255, 0)); + } + if (MidmoDaoDingGai > distanceMid ){ + textPainter.appendPaintJob("中端盖到膜的距离:" + std::to_string(halconOutput.out_Distance[3].D()) + "mm", cv::Point(50, 750), cv::Scalar(0, 0, 255)); + if (moDaoDingGaiEnable) + data->halconOK = false; + } + else{ + textPainter.appendPaintJob("中端盖到膜的距离:" + std::to_string(halconOutput.out_Distance[3].D()) + "mm", cv::Point(50, 750), cv::Scalar(0, 255, 0)); + } + if (DowmmoDaoDingGai > distanceDown ){ + textPainter.appendPaintJob("下端盖到膜的距离:" + std::to_string(halconOutput.out_Distance[4].D()) + "mm", cv::Point(50, 950), cv::Scalar(0, 0, 255)); + + if (moDaoDingGaiEnable) + data->halconOK = false; + } + else{ + textPainter.appendPaintJob("下端盖到膜的距离:" + std::to_string(halconOutput.out_Distance[4].D()) + "mm", cv::Point(50, 950), cv::Scalar(0, 255, 0)); + } + } + else { + data->halconOK = false; + textPainter.appendPaintJob("测量算法异常", cv::Point(50, 300), cv::Scalar(0, 0, 255)); + //PlcResult = "2"; + } + + + if (data->halconOK == false || !defects.empty()){ + PlcResult = "2"; + textPainter.appendPaintJob("NG", cv::Point(Paint.cols - 600, 50), cv::Scalar(0, 0, 255)); + } + else{ + textPainter.appendPaintJob("OK", cv::Point(Paint.cols - 600, 50), cv::Scalar(0, 255, 0)); + } + if (data->service->_paramSyncManager->get("上大面", "判定启用", false) == false) + PlcResult = "1"; + if (data->service->_paramSyncManager->get("通用设置","强制OK", false).toBool()) + PlcResult = "1"; + if (MidmoDaoDingGai == 0)MidmoDaoDingGai = -99; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + + "_" + QByteArray::number(batteryLength) + + "," + QByteArray::number(batteryWidth) + + "," + QByteArray::number(MidmoDaoDingGai)); + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + PlcResult); + /*try { + futrue.wait(); + auto hResult = futrue.get(); + for (int i = 0; i < hResult.drawPoints.size() && i < hResult.ngTexts.size(); i++) { + for (auto& point : hResult.drawPoints[i]) { + textPainter.appendPaintJob(point, cv::Scalar(0, 0, 255)); + } + textPainter.appendPaintJob(hResult.ngTexts[i], hResult.drawPoints[i][0], cv::Scalar(0, 0, 255)); + } + if (hResult.isNg) + data->aiDetOK = false; + } + catch (...) { + qDebug() << "上大面破损检测崩溃!!!"; + }*/ + + textPainter.executePaintJobs(Paint); + //textPainterTiny.excusePaintJobs(Paint); + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20; + cvPainter textPainter1(Rate); + textPainter1.appendPaintJob("产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainter1.appendPaintJob("获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + textPainter1.executePaintJobs(Paint); + + data->insertResultImg(Paint); + + return true; + } + CATCH_EXCEPTIONS + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(300); + textPainter.appendPaintJob("检测异常", cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("强制OK", false).toBool()) + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + + "_" + QByteArray::number(-99) + + "," + QByteArray::number(-99) + + "," + QByteArray::number(-99)); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + + //exceptionResume(data, _detParams, address, "检测异常", "包膜后上大面"); +} + + +static bool BaoMoHou_XiaDaMian(std::shared_ptr data, baoMoHouDaMianDetParams* _detParams, yh_581_address address) +{ + try { + QDateTime createTime = QDateTime::currentDateTime(); + //data->oriImgs[0] = cv::imread("D://testImage//Camera2.bmp", -1); + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + QByteArray plcResult = "1"; + + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() != 1) + { + qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); + return false; + } + /*if (data->oriImgs[0].rows == 150) { + cv::Mat Paint; + data->oriImgs[0].copyTo(Paint); + cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); + return false; + }*/ + + + //QMutex paintMutex; + clock_t start, finish; + start = clock(); + bool isLeft = address.triggerAddr == 19400 ? true : false; + + cv::Mat src = data->getOriImg(0); + cv::Mat Paint; + if (src.channels() != 3) + cv::cvtColor(src, Paint, cv::COLOR_GRAY2BGR); + else + src.copyTo(Paint); + //定位高>800 宽>6000的 + std::vector dingWeiImgs; + std::vector rects; + { + cv::Mat forDingWei; + cv::resize(src, forDingWei, cv::Size(), 0.1, 0.1); + cv::Mat dingweiSegResult = ~_detParams->dingwei_seg->Evaluate(forDingWei); + //cv::imwrite("E:/"+QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz").toStdString()+".png", dingweiSegResult); + std::vector> contours; + cv::findContours(dingweiSegResult, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + rect.width = rect.width * 10 + 10; + rect.height = rect.height * 10 + 10; + rect.x = rect.x * 10 - 10; + rect.y = rect.y * 10 - 10; + + if (rect.width > 5500 && rect.height > 700) { + dingWeiImgs.push_back(src(rect).clone()); + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 3); + rects.push_back(rect); + } + } + } + bool isNg = false; + float in_xResolution; + float in_yResolution; + if (isLeft) + { + in_xResolution = data->service->_paramSyncManager->get("上大面", "左侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("上大面", "左侧相机y方向精度", 0.029527).toDouble(); + } + else + { + in_xResolution = data->service->_paramSyncManager->get("上大面", "右侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("上大面", "右侧相机y方向精度", 0.029527).toDouble(); + } + //float in_xResolution = data->service->_paramSyncManager->get("下大面", "相机x方向精度", 1).toFloat(); + //float in_yResolution = data->service->_paramSyncManager->get("下大面", "相机y方向精度", 1).toFloat(); + float MinWidth = data->service->_paramSyncManager->get("下大面", "Seg筛选阈值宽(mm)", 50).toFloat(); + float MinHeight = data->service->_paramSyncManager->get("下大面", "Seg筛选阈值高(mm)", 50).toFloat(); + float MinArea = data->service->_paramSyncManager->get("下大面", "Seg筛选阈值面积(mm²)", 50).toFloat(); + + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + if (dingWeiImgs.size() < 2)qDebug() << "图片不满足2 " << dingWeiImgs.size(); + for (int imgIndex = 0; imgIndex < dingWeiImgs.size(); imgIndex++) { + cv::Mat forSeg; + cv::resize(dingWeiImgs[imgIndex], forSeg, cv::Size(), 0.5, 0.5); + data->insertSegOriginImg(dingWeiImgs[imgIndex]); + auto SegResult = ~_detParams->seg->Evaluate(forSeg); + std::vector> contours; + cv::findContours(SegResult, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + for (int i = 0; i < contours.size(); i++) { + cv::Rect rect = cv::boundingRect(contours[i]); + float realArea = cv::contourArea(contours[i]) * in_yResolution * in_xResolution; + float realHeight = rect.height * in_yResolution; + float realWidth = rect.width * in_xResolution; + if ((realWidth > MinWidth || realHeight > MinHeight) && (realArea > MinArea)) + { + auto clsImgs = cutImg2ClsVec(rect, forSeg); + bool clsIsNg = false; + for (auto& clsImg : clsImgs) { + auto clsResult = _detParams->cls->Evaluate(clsImg); + if (clsResult.classes == 2) + clsIsNg = true; + } + if (clsIsNg == true) { + cv::drawContours(Paint(rects[imgIndex]), contours, i, cv::Scalar(0, 0, 255), 3); + //cv::rectangle(Paint(rects[imgIndex]), rect, cv::Scalar(0, 0, 255), 3); + isNg = true; + } + for (auto& clsImg : clsImgs) + data->insertClsOriginImg({ std::make_pair(clsIsNg == true ? "NG" : "OK",clsImg) }); + // + } + } + } + cvPainter textPainter(300); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + if (isNg) + { + textPainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + } + else + textPainter.appendPaintJob("OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + + + //data->service->_missionResultHandle->addSqlData(QString::fromStdString(data->SNcode), { {"包膜后下大面四角", isNg } }); + + if (isNg) + { + data->service->_missionResultHandle->toBarChart(data->SNcode, { "包膜后下大面四角NG" }); + plcResult = "2"; + } + else + data->service->_missionResultHandle->toBarChart(data->SNcode, {}); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123,0.123"); + if (data->service->_paramSyncManager->get("下大面", "判定启用", false) == false) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); + + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20; + cvPainter textPainter1(Rate); + textPainter1.appendPaintJob("产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainter1.appendPaintJob("获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + textPainter1.executePaintJobs(Paint); + data->insertResultImg(Paint); + return true; + } + CATCH_EXCEPTIONS + //exceptionResume(data, _detParams, address, "检测异常", "包膜后下大面四角"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob("检测异常", cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123,0.0123"); + //if (data->service->_paramSyncManager->get("强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 +} + +#pragma endregion + +class _581_BaoMo : public mission { +public: + using baoMoHouShangDaMianTrigger = _581_Single_triggerTemplate; + baoMoHouShangDaMianTrigger _station_1;//shangdamian + baoMoHouShangDaMianTrigger _station_3;//shangdamian + + using baoMoHouXiaDaMianTrigger = _581_Single_triggerTemplate; + baoMoHouXiaDaMianTrigger _station_2;//xiadamian + baoMoHouXiaDaMianTrigger _station_4;//xiadamian + + std::vector _baoMoHouDaMianDetParams = std::vector< baoMoHouDaMianDetParams>(4); + detParams _detParams; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_602.hdpl", bridge); + for (int i = 0; i < _baoMoHouDaMianDetParams.size(); i++) { + _baoMoHouDaMianDetParams[i].dingwei_seg = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/BaoMoXiaDaMianSiJiaoDingWei_seg.BHD", 64, i % 2, "包膜后大面定位Seg网"); + _baoMoHouDaMianDetParams[i].seg = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/BaoMoXiaDaMianSiJiao_seg.BHD", 64, i % 2, "包膜后大面Seg网"); + _baoMoHouDaMianDetParams[i].cls = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/BaoMoXiaDaMianSiJiao_cls.BHD", 1, i % 2, "包膜后大面Cls网"); + } + } + bool initCustom() { + +#pragma region 注册触发器回调 + //baoMoHouShangDaMianTrigger::resultHandleCallback resultCallback_BaoMoHouShangDaMian = + // ([=](yh_581_address address, _581_BaoMoResult resultData, std::shared_ptr data ) { + // _serviceManager->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + // _serviceManager->_missionResultHandle->handleAllResult(data->SNcode, resultData.result, resultData.defects); + // }); + _station_1.init("Camera1", "包膜后大面外观检测-右上-相机5(上大面1)", 18800, 1, 1, &_baoMoHouDaMianDetParams[0], _serviceManager, bridge, BaoMoHou_ShangDaMian); + _station_3.init("Camera3", "包膜后大面外观检测-左上-相机7(上大面2)", 19200, 1, 1, &_baoMoHouDaMianDetParams[1], _serviceManager, bridge, BaoMoHou_ShangDaMian); + + //baoMoHouXiaDaMianTrigger::resultHandleCallback resultCallback_BaoMoHouXiaDaMian = + // ([=](yh_581_address address, _581_BaoMoResult resultData, std::shared_ptr data) { + // _serviceManager->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + // _serviceManager->_missionResultHandle->handleAllResult(data->SNcode, resultData.result, resultData.defects); + // }); + _station_2.init("Camera2", "包膜后大面外观检测-右下-相机6(下大面四角1)", 19000, 1, 1, &_baoMoHouDaMianDetParams[2], _serviceManager, bridge, BaoMoHou_XiaDaMian); + _station_4.init("Camera4", "包膜后大面外观检测-左下-相机8(下大面四角2)", 19400, 1, 1, &_baoMoHouDaMianDetParams[3], _serviceManager, bridge, BaoMoHou_XiaDaMian); +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/baoMo/604-BaoMo-Cam5678/CMakeLists.txt b/src/mission/604/baoMo/604-BaoMo-Cam5678/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam5678/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/baoMo/604-BaoMo-Cam91011/604-BaoMo-Cam91011.cpp b/src/mission/604/baoMo/604-BaoMo-Cam91011/604-BaoMo-Cam91011.cpp new file mode 100644 index 0000000..754bb1c --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam91011/604-BaoMo-Cam91011.cpp @@ -0,0 +1,942 @@ +#include <604-BaoMo-Cam91011.h> +#include + +struct baoMoShortCoatingInput { + HObject in_Image; + HObject in_ImageSeg; + HTuple in_xResolution; + HTuple in_yResolution; +}; + +struct baoMoShortCoatingOutput { + HTuple out_Distance; + HTuple out_LineCor; + HTuple out_PointCor; + HTuple out_X; + HTuple out_Y; + HTuple out_Flag; + HTuple out_NgDetails; +}; + +struct baoMoShortThicknessInput { + HObject in_Image; + HTuple in_xResolution; + HTuple in_yResolution; +}; + +struct baoMoShortThicknessOutput { + HTuple out_Distance; + HTuple out_LineCor; + HTuple out_Flag; + HTuple out_NgDetails; +}; + +struct baoMoBlueCoverInput { + HObject in_Image; + HTuple in_Parameters; +}; + +struct baoMoBlueCoverOutput { + HTuple out_Flag; + HTuple out_NgDetails; + std::vector out_VectorX; + std::vector out_VectorY; +}; + +static std::shared_ptr>& baoMoShortCoatingEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_4_Cal_Short_Coatingcover_Dist", + [](std::map& inputHObj, + std::map& inputHTuple, + const baoMoShortCoatingInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHObj["in_ImageSeg"] = in.in_ImageSeg; + inputHTuple["in_xResolution"] = in.in_xResolution; + inputHTuple["in_yResolution"] = in.in_yResolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + baoMoShortCoatingOutput& out) { + out.out_Distance = outputHTuple["out_Distance"]; + out.out_LineCor = outputHTuple["out_LineCor"]; + out.out_PointCor = outputHTuple["out_PointCor"]; + out.out_X = outputHTuple["out_X"]; + out.out_Y = outputHTuple["out_Y"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& baoMoShortThicknessEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_4_Cal_Short_Coatingcover_Dist_1", + [](std::map& inputHObj, + std::map& inputHTuple, + const baoMoShortThicknessInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_xResolution"] = in.in_xResolution; + inputHTuple["in_yResolution"] = in.in_yResolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + baoMoShortThicknessOutput& out) { + out.out_Distance = outputHTuple["out_Distance"]; + out.out_LineCor = outputHTuple["out_LineCor"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + }); + return instance; + }(); + return evaluater; +} + +static std::shared_ptr>& baoMoBlueCoverEvaluater() { + static std::shared_ptr> evaluater = []() { + auto instance = std::make_shared>(); + instance->init("_4_TR_TL_BlueCover_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const baoMoBlueCoverInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Parameters"] = in.in_Parameters; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + baoMoBlueCoverOutput& out) { + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + out.out_VectorX = outputHTupleVector["out_VectorX"]; + out.out_VectorY = outputHTupleVector["out_VectorY"]; + }); + return instance; + }(); + return evaluater; +} + +mission* createMission() { + return new _581_BaoMo(); +} +void destroyMission(mission* _mission) { + delete _mission; +} + +struct halconResult { + double houdu = -1; + double chonghedu = -1; + bool result = true; + std::string ngText; +}; + +cv::Mat cutLabelImg(cv::Mat pic, int W, int H, cv::Rect rect, int edgeType, int largeType, int color) +{ + cv::Point center; + center.x = rect.x + rect.width / 2; + center.y = rect.y + rect.height / 2; + + if (rect.width <= W && rect.height <= H) + { + if (edgeType == 0) + { + cv::Rect rec = { center.x - W / 2,center.y - H / 2,W,H }; + if (rec.x < 0) + rec.x = 0; + if (rec.y < 0) + rec.y = 0; + if (rec.x + rec.width > pic.cols) + rec.x = pic.cols - rec.width; + if (rec.y + rec.height > pic.rows) + rec.y = pic.rows - rec.height; + + cv::Mat picCut; + pic(rec).copyTo(picCut); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + + int x, y; + cv::Rect rec; + x = center.x - W / 2; + y = center.y - H / 2; + rec.x = x; + rec.y = y; + rec.width = W; + rec.height = H; + cv::Mat picCut(H, W, pic.type(), cv::Scalar(color, color, color)); + if (x < 0) + { + rec.width += rec.x; + xDiff = 0 - rec.x; + rec.x = 0; + } + if (y < 0) + { + rec.height += rec.y; + yDiff = 0 - rec.y; + rec.y = 0; + } + if (rec.x + rec.width > pic.cols) + rec.width = pic.cols - rec.x; + if (rec.y + rec.height > pic.rows) + rec.height = pic.rows - rec.y; + + pic(rec).copyTo(picCut({ xDiff, yDiff, rec.width, rec.height })); + return picCut; + } + } + else + { + if (largeType == 0) + { + int xDiff = 0; + int yDiff = 0; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rect.x < 0) + { + rect.width += rect.x; + rect.x = 0; + } + if (rect.y < 0) + { + rect.height += rect.y; + rect.y = 0; + } + if (rect.x + rect.width > pic.cols) + rect.width = pic.cols - rect.x; + if (rect.y + rect.height > pic.rows) + rect.height = pic.rows - rect.y; + + xDiff = (wTem - rect.width) * 1.0 / 2; + yDiff = (hTem - rect.height) * 1.0 / 2; + + pic(rect).copyTo(picCut({ xDiff, yDiff, rect.width, rect.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + cv::Rect rectTem; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + rectTem = { rect.x + rect.width / 2 - wTem / 2,rect.y + rect.height / 2 - hTem / 2,wTem,hTem }; + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rectTem.x < 0) + { + rectTem.width += rectTem.x; + xDiff = 0 - rectTem.x; + rectTem.x = 0; + } + if (rectTem.y < 0) + { + rectTem.height += rectTem.y; + yDiff = 0 - rectTem.y; + rectTem.y = 0; + } + if (rectTem.x + rectTem.width > pic.cols) + rectTem.width = pic.cols - rectTem.x; + if (rectTem.y + rectTem.height > pic.rows) + rectTem.height = pic.rows - rectTem.y; + + pic(rectTem).copyTo(picCut({ xDiff, yDiff, rectTem.width, rectTem.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + } + + return cv::Mat(); +} + +void ExceptionResume(std::shared_ptr data, yh_581_address address, QString why = "", QString loaction = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob(why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + QStringList resultCsv; + resultCsv << QString::fromStdString(data->SNcode) << QDateTime::currentDateTime().toString("yyyyMMddhhmmss") << loaction << why << "异常"; + //SaveLineToCSv("D:/CCDData/" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".csv", resultCsv); +} +halconResult halconAlgo1_BaoMoHouCeMian_HouDuAndChongHe(HalconCpp::HObject& inImage, std::shared_ptr data, cvPainter* textPainter, baoMoHouDaMianDetParams* _detParams, yh_581_address address, cv::Mat& src) { + auto chongheAlgoStart = std::chrono::high_resolution_clock::now();//包膜检测开始执行的时间点 + bool isInsideCamera = QString::number(address.triggerAddr).startsWith("198") ? true : false; + halconResult hResult; + cv::Mat forSeg; + cv::resize(src, forSeg, cv::Size(), 0.2, 0.2); + cv::Mat segResult = _detParams->chongHe_seg->Evaluate(forSeg); + data->insertSegOriginImg(segResult); + auto chongheSegEnd = std::chrono::high_resolution_clock::now();//Seg执行结束的时间点 + bool isOK = false; + double Width = -99; + double chonheduMax = -99; + double chonheduMin = -99; + double in_xResolution = 0.001, in_yResolution = 0.001, offset = 0; + if (isInsideCamera) { + in_xResolution = data->service->_paramSyncManager->get("包膜后侧面", "内侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("包膜后侧面", "内侧相机y方向精度", 0.029527).toDouble(); + offset = data->service->_paramSyncManager->get("补偿", "内侧相机厚度补偿", 0.00).toDouble(); + } + else { + in_xResolution = data->service->_paramSyncManager->get("包膜后侧面", "外侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("包膜后侧面", "外侧相机y方向精度", 0.029527).toDouble(); + offset = data->service->_paramSyncManager->get("补偿", "外侧相机厚度补偿", 0.00).toDouble(); + } + double limitHigh = data->service->_paramSyncManager->get("包膜后侧面", "最大厚度阈值(mm)", 200.0).toDouble(); + double limitLow = data->service->_paramSyncManager->get("包膜后侧面", "最小厚度阈值(mm)", 0.0).toDouble(); + double chonghelimitHigh = data->service->_paramSyncManager->get("包膜后侧面", "蓝膜重叠区最大值(mm)", 30.0).toDouble(); + double chonghelimitLow = data->service->_paramSyncManager->get("包膜后侧面", "蓝膜重叠区最小值(mm)", 2.0).toDouble(); + //HalconCpp::HObject in_Image = MatToHObject(src); + HalconCpp::HObject in_Image = inImage; + HalconCpp::HObject in_ImageSeg = MatToHObject(segResult); + auto chongheConvertImageEnd = std::chrono::high_resolution_clock::now();//Mat转Halcon结束的时间点 + baoMoShortCoatingInput hInput; + hInput.in_Image = in_Image; + hInput.in_ImageSeg = in_ImageSeg; + hInput.in_xResolution = in_xResolution; + hInput.in_yResolution = in_yResolution; + auto hOutput = baoMoShortCoatingEvaluater()->evaluate(hInput); + int outFlag = hOutput.runFlag ? hOutput.result.out_Flag.D() : 0; + auto chongheHalconExecuteEnd = std::chrono::high_resolution_clock::now();//hanlcon算法执行完成的时间点 + if (outFlag == 1) { + auto& halconOutput = hOutput.result; + Width = halconOutput.out_Distance[2].D(); + chonheduMin = halconOutput.out_Distance[0].D(); + chonheduMax = halconOutput.out_Distance[1].D(); + + if (Width == 0) Width = -99; + if (chonheduMin == 0) chonheduMin = -99; + if (chonheduMax == 0) chonheduMax = -99; + + //电芯厚度 + //重叠区域最小宽度 + //重叠区域最大宽度 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) \ + + "_" + QByteArray::number(Width)\ + + "," + QByteArray::number(chonheduMin)\ + + "," + QByteArray::number(chonheduMax)); + + for (int i = 0; i < halconOutput.out_LineCor.Length() / 4; i++) { + textPainter->appendPaintJob(cv::Point(halconOutput.out_LineCor[i * 4 + 1].D(), halconOutput.out_LineCor[i * 4].D()) / 2, + cv::Point(halconOutput.out_LineCor[i * 4 + 3].D(), halconOutput.out_LineCor[i * 4 + 2].D()) / 2, + cv::Scalar(0, 255, 0), 5); + } + + hResult.houdu = Width + offset; + hResult.chonghedu = chonheduMax; + QString defectStr = "电芯厚度:" + QString::number(Width, 'f', 3) + "mm"; + if (limitHigh > Width && limitLow < Width) { + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(50, 50), cv::Scalar(0, 255, 0)); + isOK = true; + } + else { + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(50, 50), cv::Scalar(0, 0, 255)); + hResult.ngText = "厚度判定不良"; + hResult.result = false; + } + + defectStr = "蓝膜重叠区最大值(mm):" + QString::number(halconOutput.out_Distance[1].D(), 'f', 3) + "mm"; + if (halconOutput.out_Distance[1].D() > chonghelimitHigh) { + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(50, 350), cv::Scalar(0, 0, 255)); + hResult.ngText = "重叠区最大值NG"; + hResult.result = false; + } + else { + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(50, 350), cv::Scalar(0, 255, 0)); + } + defectStr = "蓝膜重叠区最小值(mm):" + QString::number(halconOutput.out_Distance[0].D(), 'f', 3) + "mm"; + if (halconOutput.out_Distance[0].D() < chonghelimitLow) { + hResult.ngText = "重叠区最小值NG"; + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(50, 200), cv::Scalar(0, 0, 255)); + hResult.result = false; + } + else { + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(50, 200), cv::Scalar(0, 255, 0)); + } + for (int i = 0; i < halconOutput.out_X.Length() && i < halconOutput.out_Y.Length(); i++) { + textPainter->appendPaintJob(cv::Point(halconOutput.out_X[i].D(), halconOutput.out_Y[i].D()) / 2, 2, cv::Scalar(0, 255, 0), 2, 0); + } + for (int i = 0; i < halconOutput.out_PointCor.Length() / 2 - 1; i++) { + textPainter->appendPaintJob(cv::Point(halconOutput.out_PointCor[i * 2 + 1].D(), halconOutput.out_PointCor[i * 2].D()) / 2, 2, cv::Scalar(0, 255, 0), 3, 0); + } + } + else { + //电芯厚度 + //重叠区域最小宽度 + //重叠区域最大宽度 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) \ + + "_" + QByteArray::number(Width)\ + + "," + QByteArray::number(chonheduMin)\ + + "," + QByteArray::number(chonheduMax)); + + std::string ngText = hOutput.runFlag ? gbkToUtf8(hOutput.result.out_NgDetails.ToString().Text()) : hOutput.faildResult; + textPainter->appendPaintJob(ngText, cv::Point(50, 200), cv::Scalar(0, 0, 255)); + data->halconOK = false; + hResult.result = false; + hResult.ngText = ngText; + } + + auto chongheAlgoEnd = std::chrono::high_resolution_clock::now();//包膜重合检测完成的时间点 +#pragma region 打印耗时 + auto detectTotalTime = std::chrono::duration_cast(chongheAlgoEnd - chongheAlgoStart); + auto segTime = std::chrono::duration_cast(chongheSegEnd - chongheAlgoStart); + auto convertImageTime = std::chrono::duration_cast(chongheConvertImageEnd - chongheSegEnd); + auto halconExecuteTime = std::chrono::duration_cast(chongheHalconExecuteEnd - chongheConvertImageEnd); + auto paintResultTime = std::chrono::duration_cast(chongheAlgoEnd - chongheHalconExecuteEnd); + + qDebug() << __FUNCTION__ << " line:" << __LINE__ + << ", SN" << QString::fromStdString(data->SNcode) + << "包膜重合检测总耗时:" << detectTotalTime.count() << "ms,\n" + << "包膜重合seg耗时:" << segTime.count() << "ms,\n" + << "包膜重合检测Mat转Halcon耗时:" << convertImageTime.count() << "ms,\n" + << "包膜重合检测Halcon执行耗时:" << halconExecuteTime.count() << "ms,\n" + << "包膜重合检测结果绘制耗时:" << paintResultTime.count() << "ms"; +#pragma endregion + + return hResult; +} +halconResult halconAlgo1_BaoMoHouCeMian_HouDu(HalconCpp::HObject& inImage, std::shared_ptr data, cvPainter* textPainter, yh_581_address address, cv::Mat& src) { + auto houduAlgoStart = std::chrono::high_resolution_clock::now();//包膜后厚度检测算法开始时间点 + bool isInsideCamera = QString::number(address.triggerAddr).startsWith("198") ? true : false; + halconResult hResult; + bool isOK = false; + double Width = -99; + double in_xResolution = 0.001, in_yResolution = 0.001, offset = 0; + if (isInsideCamera) { + in_xResolution = data->service->_paramSyncManager->get("包膜后侧面", "内侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("包膜后侧面", "内侧相机y方向精度", 0.029527).toDouble(); + offset = data->service->_paramSyncManager->get("补偿", "内侧相机厚度补偿", 0.00).toDouble(); + } + else { + in_xResolution = data->service->_paramSyncManager->get("包膜后侧面", "外侧相机x方向精度", 0.0308812).toDouble(); + in_yResolution = data->service->_paramSyncManager->get("包膜后侧面", "外侧相机y方向精度", 0.029527).toDouble(); + offset = data->service->_paramSyncManager->get("补偿", "外侧相机厚度补偿", 0.00).toDouble(); + } + double limitHigh = data->service->_paramSyncManager->get("包膜后侧面", "最大厚度阈值(mm)", 200.0).toFloat(); + double limitLow = data->service->_paramSyncManager->get("包膜后侧面", "最小厚度阈值(mm)", 0.0).toFloat(); + //HalconCpp::HObject in_Image = MatToHObject(src); + HalconCpp::HObject in_Image = inImage; + auto houduConvertImageEnd = std::chrono::high_resolution_clock::now();//Mat转Halcon结束的时间点 + baoMoShortThicknessInput hInput; + hInput.in_Image = in_Image; + hInput.in_xResolution = HTuple(in_xResolution); + hInput.in_yResolution = HTuple(in_yResolution); + auto hOutput = baoMoShortThicknessEvaluater()->evaluate(hInput); + int outFlag = hOutput.runFlag ? hOutput.result.out_Flag.D() : 0; + auto houduHalconExecuteEnd = std::chrono::high_resolution_clock::now();//hanlcon算法执行完成的时间点 + if (outFlag == 1) { + auto& halconOutput = hOutput.result; + Width = halconOutput.out_Distance.D(); + //data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(Width));//算法成功发送真实值 + + //电芯厚度 + //重叠区域最小宽度 + //重叠区域最大宽度 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) \ + + "_" + QByteArray::number(Width)\ + + "," + QByteArray::number(-99)\ + + "," + QByteArray::number(-99)); + + for (int i = 0; i < halconOutput.out_LineCor.Length() / 4; i++) { + textPainter->appendPaintJob(cv::Point(halconOutput.out_LineCor[i * 4 + 1].D(), halconOutput.out_LineCor[i * 4].D()) / 2, + cv::Point(halconOutput.out_LineCor[i * 4 + 3].D(), halconOutput.out_LineCor[i * 4 + 2].D()) / 2, + cv::Scalar(0, 255, 0), 5); + } + hResult.houdu = Width; + QString defectStr = "电芯厚度:" + QString::number(Width, 'f', 3) + "mm"; + if (limitHigh > Width && limitLow < Width) { + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(50, 50), cv::Scalar(0, 255, 0)); + isOK = true; + } + else { + textPainter->appendPaintJob(defectStr.toStdString(), cv::Point(50, 50), cv::Scalar(0, 0, 255)); + hResult.ngText = "厚度判定不良"; + hResult.result = false; + } + } + else { + //data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(Width));//算法失败发送-99 + //电芯厚度 + //重叠区域最小宽度 + //重叠区域最大宽度 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) \ + + "_" + QByteArray::number(Width)\ + + "," + QByteArray::number(-99)\ + + "," + QByteArray::number(-99)); + std::string ngText = hOutput.runFlag ? gbkToUtf8(hOutput.result.out_NgDetails.ToString().Text()) : hOutput.faildResult; + textPainter->appendPaintJob(ngText, cv::Point(50, 200), cv::Scalar(0, 0, 255)); + data->halconOK = false; + hResult.result = false; + hResult.ngText = ngText; + } + + auto houduAlgoEnd = std::chrono::high_resolution_clock::now();//包膜后厚度检测完成的时间点 + +#pragma region 打印耗时 + auto detectTotalTime = std::chrono::duration_cast(houduAlgoEnd - houduAlgoStart); + auto convertImageTime = std::chrono::duration_cast(houduConvertImageEnd - houduAlgoStart); + auto halconExecuteTime = std::chrono::duration_cast(houduHalconExecuteEnd - houduConvertImageEnd); + auto paintResultTime = std::chrono::duration_cast(houduAlgoEnd - houduHalconExecuteEnd); + + qDebug() << __FUNCTION__ << " line:" << __LINE__ + << ", SN" << QString::fromStdString(data->SNcode) + << "包膜厚度检测总耗时:" << detectTotalTime.count() << "ms,\n" + << "包膜厚度检测Mat转Halcon耗时:" << convertImageTime.count() << "ms,\n" + << "包膜厚度检测Halcon执行耗时:" << halconExecuteTime.count() << "ms,\n" + << "包膜厚度检测结果绘制耗时:" << paintResultTime.count() << "ms"; +#pragma endregion + + return hResult; +} +bool halconAlgo1_BaoMoHouCeMian_PoSun(HalconCpp::HObject& inImage, std::shared_ptr data, cvPainter* textPainter, cvPainter* textPainterCls, yh_581_address address, cv::Mat& src, cv::Mat& Paint, HObject& outImage, const std::shared_ptr& clsPtr) { + auto posunAlgoStart = std::chrono::high_resolution_clock::now();//破损检测算法开始时间点 + bool isOK = true; + try { + HalconCpp::HObject in_Image = inImage; + auto posunConvertImageEnd = std::chrono::high_resolution_clock::now();//Mat转Halcon结束的时间点 + HTuple in_Parameters; + in_Parameters[0] = data->service->_paramSyncManager->get("包膜后侧面", "灰度管控阈值", 230).toInt(); + in_Parameters[1] = data->service->_paramSyncManager->get("包膜后侧面", "缺陷像素数量管控阈值(pix)", 300000).toInt(); + in_Parameters[2] = data->service->_paramSyncManager->get("包膜后侧面", "缺陷的宽度管控阈值(pix)", 600).toInt(); + in_Parameters[3] = data->service->_paramSyncManager->get("包膜后侧面", "缺陷的高度管控阈值(pix)", 60).toInt(); + + int limitWidth = data->service->_paramSyncManager->get("包膜后侧面", "缺陷的宽度管控阈值(pix)", 600).toInt(); + int limitHeight = data->service->_paramSyncManager->get("包膜后侧面", "缺陷的高度管控阈值(pix)", 60).toInt(); + + double probThreshold = data->service->_paramSyncManager->get("hide", "分类模型置信度阈值(下限)", 0.9).toDouble();//分类模型置信度下限 + + baoMoBlueCoverInput hInput; + hInput.in_Image = in_Image; + hInput.in_Parameters = in_Parameters; + auto hOutput = baoMoBlueCoverEvaluater()->evaluate(hInput); + auto posunHalconExecuteEnd = std::chrono::high_resolution_clock::now();//hanlcon算法执行完成的时间点 + int outFlag = hOutput.runFlag ? hOutput.result.out_Flag.D() : 0; + std::vector> clsImgs; + if (outFlag == 1) { + textPainter->appendPaintJob("破损检测OK", cv::Point(2550, 50), cv::Scalar(0, 255, 0)); + } + else { + //在进行一次CLS复判 + std::vector rects; + std::vector< std::vector>rectPoints;//此不可直接使用drawContour绘制 + auto& halconOutput = hOutput.result; + for (int i = 0; i < halconOutput.out_VectorX.size() && i < halconOutput.out_VectorY.size(); i++) { + std::vector points; + HalconCpp::HTuple vectorX = halconOutput.out_VectorX[i]; + HalconCpp::HTuple vectorY = halconOutput.out_VectorY[i]; + for (int o = 0; o < vectorX.Length() && o < vectorY.Length() && vectorX.Length() == vectorY.Length(); o++) { + points.push_back(cv::Point(vectorX[o].D(), vectorY[o].D())); + } + if (points.empty()) + continue; + rects.push_back(cv::boundingRect(points)); + rectPoints.push_back(points); + } + + for (int i = 0; i < rects.size(); i++) { + cv::Rect rect = rects[i]; + + if (rect.width == 0 || rect.height == 0) + continue; + + cv::Rect Paintrect(rect.x / 2, rect.y / 2, rect.width / 2, rect.height / 2); + cv::Mat clsImg = cutLabelImg(src, 128, 128, rect, 1, 1, 0); + auto clsResult = clsPtr->Evaluate(clsImg); + int clsReult = clsResult.classes; + double prob = clsResult.prob; + clsImgs.push_back({ "result" + std::to_string(clsReult) + "_prob" + std::to_string(prob),clsImg }); + if (clsReult == 1 && prob > probThreshold && rect.width > limitWidth && rect.height > limitHeight) { + textPainter->appendPaintJob(Paintrect, cv::Scalar(0, 0, 255), 3); + isOK = false; + } + else + textPainter->appendPaintJob(Paintrect, cv::Scalar(0, 255, 0), 3); + + textPainterCls->appendPaintJob( + "w:" + std::to_string(rect.width) + + " h:" + std::to_string(rect.height) + + " r:" + std::to_string(clsReult) + + " p:" + std::to_string(prob), cv::Point(rect.x, rect.y) / 2, cv::Scalar(0, 255, 255)); + } + if (isOK == false) { + std::string ngDetailStr = hOutput.runFlag ? gbkToUtf8(hOutput.result.out_NgDetails.ToString().Text()) : hOutput.faildResult; + textPainter->appendPaintJob(ngDetailStr.substr(1, ngDetailStr.length() - 2), cv::Point(2050, 200), cv::Scalar(0, 0, 255)); + textPainter->appendPaintJob("破损检测NG", cv::Point(2550, 50), cv::Scalar(0, 0, 255)); + data->halconOK = false; + } + else { + textPainter->appendPaintJob("破损检测OK", cv::Point(2550, 50), cv::Scalar(0, 255, 0)); + } + } + data->insertClsOriginImg(clsImgs); + + auto posunAlgoEnd = std::chrono::high_resolution_clock::now();//包膜后厚度检测完成的时间点 + +#pragma region 打印耗时 + auto detectTotalTime = std::chrono::duration_cast(posunAlgoEnd - posunAlgoStart); + auto convertImageTime = std::chrono::duration_cast(posunConvertImageEnd - posunAlgoStart); + auto halconExecuteTime = std::chrono::duration_cast(posunHalconExecuteEnd - posunConvertImageEnd); + auto paintResultTime = std::chrono::duration_cast(posunAlgoEnd - posunHalconExecuteEnd); + + qDebug() << __FUNCTION__ << " line:" << __LINE__ + << ", SN" << QString::fromStdString(data->SNcode) + << ", 破损检测总耗时:" << detectTotalTime.count() << "ms,\n" + << ", 破损检测Mat转Halcon耗时:" << convertImageTime.count() << "ms,\n" + << ", 破损检测Halcon执行耗时:" << halconExecuteTime.count() << "ms,\n" + << ", 破损检测结果绘制耗时:" << paintResultTime.count() << "ms"; +#pragma endregion + + return isOK; + } + CATCH_EXCEPTIONS; + //cv::resize(src, Paint, cv::Size(), 0.5, 0.5); + if (src.channels() == 1) + cv::cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + else + src.copyTo(Paint); + return isOK; +} + +bool ALGO_BaoMoHouCeMian(std::shared_ptr data, baoMoHouDaMianDetParams* _detParams, yh_581_address address) { + try { + auto algoStart = std::chrono::high_resolution_clock::now();//包膜侧面检测开始时间点 + QDateTime createTime = QDateTime::currentDateTime(); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "SN" << QString::fromStdString(data->SNcode) << "algo start."; + QByteArray PlcResult = "1"; + std::vector < std::string> defects; + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() < 1) + { + qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); + return false; + } + cv::Mat Paint; + cv::Mat src = data->getOriImg(0); + + cvPainter textPainter(150); + cvPainter textPainterCls(35); + HObject outImage; + HalconCpp::HObject inImage = MatToHObject(src); + std::future handleAiFuture1 = std::async(std::launch::async, halconAlgo1_BaoMoHouCeMian_PoSun, std::ref(inImage), data, &textPainter, &textPainterCls, address, std::ref(src), std::ref(Paint), std::ref(outImage), _detParams->ceMian_cls); + std::future handleAiFuture2; + if (address.triggerAddr == 19602 || address.triggerAddr == 19800) { + handleAiFuture2 = std::async(std::launch::async, halconAlgo1_BaoMoHouCeMian_HouDuAndChongHe, std::ref(inImage), data, &textPainter, _detParams, address, std::ref(src)); + } + else { + handleAiFuture2 = std::async(std::launch::async, halconAlgo1_BaoMoHouCeMian_HouDu, std::ref(inImage), data, &textPainter, address, std::ref(src)); + } + + //textPainter.putText(Paint, "电芯厚度:" + std::to_string(Width) + "mm", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + + handleAiFuture1.wait(); + bool res1 = handleAiFuture1.get(); + + handleAiFuture2.wait(); + halconResult res2 = handleAiFuture2.get(); + + if (res1 == false) { + defects.push_back("侧面破损"); + PlcResult = "2"; + data->halconOK = false; + } + if (res2.result == false) { + defects.push_back(res2.ngText); + PlcResult = "2"; + data->halconOK = false; + } + + if (data->service->_paramSyncManager->get("包膜后侧面", "判定启用", false) == false) + PlcResult = "1"; + + /*if (address.triggerAddr == 19602 || address.triggerAddr == 19800) + { + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr+2) + "_" + QByteArray::number(res2.houdu)); + } + else + { + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(res2.houdu)); + }*/ + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + PlcResult);//发送结果。该动作尽可能往前放,为数据发送争取时间 + + //画结果图 + //Paint = HObject2Mat(outImage); + if (Paint.empty() || Paint.rows == 0) { + if (src.channels() != 3) + cv::cvtColor(src, Paint, cv::COLOR_GRAY2BGR); + else + src.copyTo(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + } + else + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + textPainter.executePaintJobs(Paint); + textPainterCls.executePaintJobs(Paint); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20; + cvPainter textPainter1(Rate); + textPainter1.appendPaintJob("产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainter1.appendPaintJob("获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + textPainter1.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + + auto algoEnd = std::chrono::high_resolution_clock::now(); + auto detectTime = std::chrono::duration_cast(algoEnd - algoStart); + qDebug() << __FUNCTION__ << " line:" << __LINE__ + << ", SN" << QString::fromStdString(data->SNcode) + << ", 包膜侧面检测部分总耗时:" << detectTime.count() << "ms"; + + return true; + } + CATCH_EXCEPTIONS; + ExceptionResume(data, address, "检测异常", "包膜后侧面"); + return true; +} + +bool ALGO_BaoMoHouXiaDaMian(std::shared_ptr data, xiaDaMianParam* _detParams, yh_581_address address) { + try { + auto algoStart = std::chrono::high_resolution_clock::now();//大面检测开始时间点 + QDateTime createTime = QDateTime::currentDateTime(); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "SN" << QString::fromStdString(data->SNcode) << "algo start."; + std::vector < std::string> defects; + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() < 1) { + qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); + return false; + } + cv::Mat Paint; + + //data->oriImgs[0] = cv::imread("D:/testImg/xiadamian3.bmp", -1); + + cv::Mat src = data->getOriImg(0); + QMutex mutex; + //data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1"); + cv::Mat afterDingWei; + cv::Rect dingWeiRect; + { + cv::Mat forDingWei; + cv::resize(src, forDingWei, cv::Size(), 0.1, 0.1); + data->insertSegOriginImg(forDingWei); + auto dingweiSegStart = std::chrono::high_resolution_clock::now(); + cv::Mat SegResult = ~_detParams->dinggWei_seg->Evaluate(forDingWei); + auto dingweiSegEnd = std::chrono::high_resolution_clock::now(); + auto dingweiSegTime = std::chrono::duration_cast(dingweiSegEnd - dingweiSegStart); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "包膜后下大面产品定位Seg处理耗时:" << dingweiSegTime.count() << "ms"; + //cv::imwrite("D:\\seg\\"+QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz").toStdString() + data->SNcode+".png", SegResult); + dingWeiRect = findMaxAreaContour(SegResult); + dingWeiRect.x *= 10; dingWeiRect.y *= 10; dingWeiRect.width *= 10; dingWeiRect.height *= 10; + src(dingWeiRect).copyTo(afterDingWei); + if (dingWeiRect.width < 3500 || dingWeiRect.height < 3500) { + data->insertSegOriginImg(SegResult); + ExceptionResume(data, address, "定位失败"); + return false; + } + } + cv::resize(afterDingWei, afterDingWei, cv::Size(), 0.5, 0.5); + if (afterDingWei.channels() != 3) + cv::cvtColor(afterDingWei, Paint, cv::COLOR_GRAY2BGR); + else + afterDingWei.copyTo(Paint); + data->insertSegOriginImg(afterDingWei); + auto segPtr = (address.triggerAddr == 20000 ? _detParams->qiPao_seg_gpu0 : _detParams->qiPao_seg_gpu1); + auto clsPtr = (address.triggerAddr == 20000 ? _detParams->qiPao_cls_gpu0 : _detParams->qiPao_cls_gpu1); + auto detectSegStart = std::chrono::high_resolution_clock::now(); + cv::Mat SegResult = ~segPtr->Evaluate(afterDingWei); + auto detectSegEnd = std::chrono::high_resolution_clock::now(); + auto detectSegTime = std::chrono::duration_cast(detectSegEnd - detectSegStart); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "包膜后下大面缺陷检测Seg处理耗时:" << detectSegTime.count() << "ms"; + data->insertSegOriginImg(SegResult); + std::vector> contours; + cv::findContours(SegResult, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + bool isNg = false; + double yPix = data->service->_paramSyncManager->get("下大面", "相机y方向精度", 0.08389).toDouble(); + double xPix = data->service->_paramSyncManager->get("下大面", "相机x方向精度", 0.06746).toDouble(); + cvPainter textPainterCls(40); + int fillterAndWidth = data->service->_paramSyncManager->get("下大面", "下大面过滤宽度(与)(mm)", 25.00).toDouble() / xPix; + int fillterAndHeight = data->service->_paramSyncManager->get("下大面", "下大面过滤高度(与)(mm)", 25.00).toDouble() / yPix; + int fillterOr = data->service->_paramSyncManager->get("下大面", "下大面过滤(或)(mm)", 50.00).toDouble() / yPix; + float thresholdRate = data->service->_paramSyncManager->get("下大面", "下大面过滤百分比(%)", 50.00).toFloat(); + + float pixArea = 0; + std::vector> drawDefectContours; + std::vector> drawOKContours; + + for (int ii = 0; ii < contours.size(); ii++) { + cv::Rect rect = cv::boundingRect(contours[ii]); + cv::RotatedRect rRect = cv::minAreaRect(contours[ii]); + + float rWidth = rRect.size.width; + float rHeight = rRect.size.height; + if (rWidth > rHeight) { + float tempValue = rWidth; + rWidth = rHeight; + rHeight = tempValue; + } + + float realArea = cv::contourArea(contours[ii]) * yPix * xPix; + float realHeight = rect.height * yPix; + float realWidth = rect.width * xPix; + if (realWidth > fillterAndWidth && realHeight > fillterAndHeight && (realHeight > fillterOr || realWidth > fillterOr)) { + std::vector clsMatVec; + if (rHeight / rWidth > 20) { + clsMatVec = cutImg2ClsVec(rect, afterDingWei); + } + else { + cv::Mat clsMat = cutLabelImg(afterDingWei, 128, 128, rect, 1, 1, 0); + clsMatVec.push_back(clsMat); + } + + for (int mm = 0; mm < clsMatVec.size(); mm++) { + auto clsResult = clsPtr->Evaluate(clsMatVec[mm]); + switch (clsResult.classes) { + case 0: { + //OK + break; + } + case 1: { + //长气泡 + pixArea += cv::contourArea(contours[ii]); + drawDefectContours.push_back(contours[ii]); + QString defectStr = "缺陷宽:" + QString::number(realWidth, 'f', 3) + "mm高:" + QString::number(realHeight, 'f', 3) + "mm面积:" + QString::number(realArea, 'f', 3) + "mm²"; + textPainterCls.appendPaintJob(defectStr.toStdString(), cv::Point(rect.x + rect.width / 2, rect.y + rect.height / 2), cv::Scalar(0, 255, 0)); + //cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + break; + } + case 2: { + //圆气泡 + drawOKContours.push_back(contours[ii]); + break; + } + case 3: { + //白气泡 + drawOKContours.push_back(contours[ii]); + break; + } + case 4: { + //其他 + drawOKContours.push_back(contours[ii]); + break; + } + default: + break; + } + } + } + } + + float pixAreaRate = pixArea / (afterDingWei.cols * afterDingWei.rows) * 100; + if (pixAreaRate > thresholdRate) { + for (int nn = 0; nn < drawDefectContours.size(); nn++) { + cv::drawContours(Paint, drawDefectContours, nn, cv::Scalar(0, 0, 255), 2); + cv::rectangle(Paint, cv::boundingRect(drawDefectContours[nn]), cv::Scalar(0, 0, 255), 3); + } + isNg = true; + } + else { + for (int nn = 0; nn < drawDefectContours.size(); nn++) { + cv::drawContours(Paint, drawDefectContours, nn, cv::Scalar(0, 255, 0), 2); + cv::rectangle(Paint, cv::boundingRect(drawDefectContours[nn]), cv::Scalar(0, 255, 0), 3); + } + } + + for (int nn = 0; nn < drawOKContours.size(); nn++) { + cv::drawContours(Paint, drawOKContours, nn, cv::Scalar(0, 255, 0), 2); + } + + int Rate = (Paint.rows > Paint.cols ? Paint.cols : Paint.rows) / 20; + + QByteArray plcResult = "1"; + cvPainter textPainter(300); + if (isNg == false) { + textPainter.appendPaintJob("OK", cv::Point(500, 50), cv::Scalar(0, 255, 0)); + //data->service->_missionResultHandle->toBarChart(data->SNcode, isNg, { }); + } + else { + defects.push_back("包膜下大面气泡"); + textPainter.appendPaintJob("NG", cv::Point(500, 50), cv::Scalar(0, 0, 255)); + plcResult = "2"; + //data->service->_missionResultHandle->toBarChart(data->SNcode, isNg, { "包膜后下大面NG" }); + } + if (data->service->_paramSyncManager->get("下大面", "下大面外观检测启用", false).toBool() == false) + plcResult = "1"; + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); + + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + + textPainterCls.executePaintJobs(Paint); + textPainter.executePaintJobs(Paint); + cvPainter textPainter1(Rate); + textPainter1.appendPaintJob("气泡面积占比:" + QString::number(pixAreaRate, 'f', 2).toStdString() + "%", cv::Point(50, 500), cv::Scalar(0, 255, 255)); + textPainter1.appendPaintJob("产品条码:" + data->SNcode, cv::Point(50, Paint.rows - Rate), cv::Scalar(0, 255, 255)); + textPainter1.appendPaintJob("获取图像时间:" + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - (Rate * 2)), cv::Scalar(0, 255, 255)); + textPainter1.executePaintJobs(Paint); + + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + + data->sqlVec.insert({ "下大面气泡面积占比", QString::number(pixAreaRate, 'f', 2) + "%" }); + + auto algoEnd = std::chrono::high_resolution_clock::now();//包膜后下大面检测结束时间点 + auto detectTime = std::chrono::duration_cast(algoEnd - algoStart); + qDebug() << __FUNCTION__ << " line:" << __LINE__ + << ", SN" << QString::fromStdString(data->SNcode) + << ", 包膜后下大面检测部分总耗时:" << detectTime.count() << "ms"; + + return true; + } + CATCH_EXCEPTIONS; + ExceptionResume(data, address, "检测异常", "包膜后下大面"); + return true; +} \ No newline at end of file diff --git a/src/mission/604/baoMo/604-BaoMo-Cam91011/604-BaoMo-Cam91011.h b/src/mission/604/baoMo/604-BaoMo-Cam91011/604-BaoMo-Cam91011.h new file mode 100644 index 0000000..e29b6cd --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam91011/604-BaoMo-Cam91011.h @@ -0,0 +1,82 @@ +#include +#include +#include +class baoMoHouDaMianDetParams { +public: + std::shared_ptr chongHe_seg; + std::shared_ptr ceMian_cls; +}; +class xiaDaMianParam { +public: + std::shared_ptr dinggWei_seg = nullptr; + std::shared_ptr qiPao_seg_gpu0 = nullptr; + std::shared_ptr qiPao_seg_gpu1 = nullptr; + std::shared_ptr qiPao_cls_gpu0 = nullptr; + std::shared_ptr qiPao_cls_gpu1 = nullptr; +}; +#pragma region 算法 +#include +#include +#include +#include +using namespace HDevEngineCpp; + +static bool ALGO_BaoMoHouXiaDaMian(std::shared_ptr data, xiaDaMianParam* _detParams, yh_581_address address); +static bool ALGO_BaoMoHouCeMian(std::shared_ptr data, baoMoHouDaMianDetParams* _detParams,yh_581_address address); +#pragma endregion + +class _581_BaoMo : public mission { +public: + using baoMoHouDaMianTrigger = _581_Union_triggerTemplate; + baoMoHouDaMianTrigger _station_1; + baoMoHouDaMianTrigger _station_2; + std::vector < baoMoHouDaMianDetParams> _baoMoHouDaMianDetParams = std::vector < baoMoHouDaMianDetParams>(2); + + _581_Union_triggerTemplate _station_3; + xiaDaMianParam _xiaDaMianParam; + + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + if (initOnce == false) { + _xiaDaMianParam.dinggWei_seg = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/xiaDaMianDingWei_seg.BHD",16,0,"包膜下大面定位Seg网"); + _xiaDaMianParam.qiPao_seg_gpu0 = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/qiPao_seg.BHD", 16, 0, "包膜气泡Seg网0"); + _xiaDaMianParam.qiPao_seg_gpu1 = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/qiPao_seg.BHD", 16, 1, "包膜气泡Seg网1"); + _xiaDaMianParam.qiPao_cls_gpu0 = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/qiPao_cls.BHD", 1, 0, "包膜气泡Cls网0"); + _xiaDaMianParam.qiPao_cls_gpu1 = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/qiPao_cls.BHD", 1, 1, "包膜气泡Cls网1"); + + _baoMoHouDaMianDetParams[0].chongHe_seg = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/chongHe_seg.BHD", 16, 0, "包膜侧面重合Seg网0"); + _baoMoHouDaMianDetParams[1].chongHe_seg = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/chongHe_seg.BHD", 16, 1, "包膜侧面重合Seg网1"); + _baoMoHouDaMianDetParams[0].ceMian_cls = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/ceMian_cls.BHD", 1, 0, "包膜侧面Cls网0"); + _baoMoHouDaMianDetParams[1].ceMian_cls = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/ceMian_cls.BHD", 1, 1, "包膜侧面Cls网1"); + initOnce = true; + } + } + bool initCustom() { + +#pragma region 注册触发器回调 + + _station_1.init("Camera1","包膜后侧面贴胶错位检测-外-相机9",19600,2,1,&_baoMoHouDaMianDetParams[0], _serviceManager, bridge, ALGO_BaoMoHouCeMian); + + + _station_2.init("Camera2", "包膜后侧面贴胶错位检测-内-相机10",19800,2,1, &_baoMoHouDaMianDetParams[1], _serviceManager, bridge, ALGO_BaoMoHouCeMian); + + + _station_3.init("Camera3", "包膜后外观检测大面相机11(下大面)",20000,2,1, &_xiaDaMianParam, _serviceManager, bridge, ALGO_BaoMoHouXiaDaMian); + +#pragma endregion + return true; + } + void reloadAlgo(QByteArray& byteArray) { + + } + + //9600 + +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/baoMo/604-BaoMo-Cam91011/CMakeLists.txt b/src/mission/604/baoMo/604-BaoMo-Cam91011/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/baoMo/604-BaoMo-Cam91011/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/baoMo/CMakeLists.txt b/src/mission/604/baoMo/CMakeLists.txt new file mode 100644 index 0000000..f9a95fd --- /dev/null +++ b/src/mission/604/baoMo/CMakeLists.txt @@ -0,0 +1,11 @@ +# CMakeList.txt: 遍历文件夹 添加子项目 + +set(OUR_LIBS "") +SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF) +message("OUR_LIBS :" ${OUR_LIBS}) + + +# 遍历子库 +foreach(dll ${OUR_LIBS}) + add_subdirectory(${dll}) +endforeach(dll ${}) diff --git a/src/mission/604/yuHan/604_YuHan_hanhou/604_YuHan_hanhou.cpp b/src/mission/604/yuHan/604_YuHan_hanhou/604_YuHan_hanhou.cpp new file mode 100644 index 0000000..64dca8f --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_hanhou/604_YuHan_hanhou.cpp @@ -0,0 +1,641 @@ +#include <604_YuHan_hanhou.h> + +#include + +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + TextRendererZH textPainter(Paint.cols, Paint.rows, textSize); + textPainter.putText(Paint, "SN:" + sn + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); +} +void handleAiProcess(cv::Mat& src, cv::Mat& segRes, cvPainter& defectPainter, + bool& silieResult, bool& fanZheResult, + cv::Point offset, std::shared_ptr clsPtr, std::vector>& clsImgs, + cv::Size fillter = cv::Size(200, 200), double probThreshold = 0.9) { + + std::vector> contours; + cv::findContours(segRes, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + int cnt = 0; + for (int i = 0; i < contours.size(); i++) { + cv::Rect originRect = cv::boundingRect(contours[i]); + cv::Rect paintRect = originRect; + paintRect.x += offset.x; + paintRect.y += offset.y; + double maxNgProb = 0.0; + int ngResultNumber = 0; + if (originRect.width < fillter.width || originRect.height < fillter.height) + continue; + std::vector clsImgVec = cutImg2ClsVec(originRect, src); + bool isNg = false; + for (auto& clsImg : clsImgVec) { + auto Result = clsPtr->Evaluate(clsImg); + if (Result.classes == 1 && Result.prob > probThreshold) { + ngResultNumber = Result.classes; + isNg = true; + maxNgProb = std::max(maxNgProb, Result.prob); + } + clsImgs.push_back({ "result" + std::to_string(Result.classes) + "_prob" + std::to_string(Result.prob) ,clsImg }); + } + { + cv::Mat testCls; + cv::resize(src(originRect), testCls, cv::Size(128, 128)); + auto Result = clsPtr->Evaluate(testCls); + if (Result.classes == 1 && Result.prob > probThreshold) { + ngResultNumber = Result.classes; + isNg = true; + maxNgProb = std::max(maxNgProb, Result.prob); + } + clsImgs.push_back({ "testClsResult" + std::to_string(Result.classes) + "_prob" + std::to_string(Result.prob),testCls }); + } + if (isNg == true) + { + defectPainter.appendPaintJob(contours, i, cv::Scalar(0, 0, 255, 30), 3, cv::LINE_8, cv::noArray(), INT_MAX, cv::Point(offset.x, offset.y)); + defectPainter.appendPaintJob("NGw" + std::to_string(originRect.width) + + "_h" + std::to_string(originRect.height) + + "_a" + std::to_string(cv::contourArea(contours[i])) + + "_r" + std::to_string(ngResultNumber) + + "_p" + std::to_string(maxNgProb) + , cv::Point(paintRect.x, paintRect.y), cv::Scalar(255, 255, 0)); + silieResult = false; + } + else + { + defectPainter.appendPaintJob(contours, i, cv::Scalar(0, 255, 0, 30), 3, cv::LINE_8, cv::noArray(), INT_MAX, cv::Point(offset.x, offset.y)); + defectPainter.appendPaintJob("OKw" + std::to_string(originRect.width) + + "_h" + std::to_string(originRect.height) + + "_a" + std::to_string(cv::contourArea(contours[i])) + + "_r" + std::to_string(ngResultNumber) + + "_p" + std::to_string(maxNgProb) + , cv::Point(paintRect.x, paintRect.y), cv::Scalar(255, 255, 0)); + } + } +} +halconResult halconAlgoProcess(const cv::Mat& img, std::shared_ptr seg_hanYinZhuaQu, cv::Mat& segResult, + const double& Resolution, const int& lineType, std::shared_ptr>& halconEvaluator) { + + halconResult result; + cv::Mat forSeg; + cv::resize(img, forSeg, cv::Size(), 0.2, 0.2); + segResult = seg_hanYinZhuaQu->Evaluate(forSeg); + cv::resize(segResult, segResult, cv::Size(), 5, 5); + HObject in_Image1 = MatToHObject(img), in_Image2 = MatToHObject(segResult); + HTuple in_Resolution(Resolution); + halconInput hInput; + hInput.in_Image1 = in_Image1; + hInput.in_Image2 = in_Image2; + hInput.in_Resolution = in_Resolution; + hInput.in_MachineType = lineType; + auto hOutput = halconEvaluator->evaluate(hInput); + result.algoCostTime = hOutput.costTime; + if (hOutput.runFlag == true && hOutput.result.out_Flag.D() == 1) + { + auto& hResult = hOutput.result; + for (auto i = 0; i < hResult.out_LinePoint.Length(); i += 4) + { + result.lines.push_back({ cv::Point(hResult.out_LinePoint[i + 1].D(), hResult.out_LinePoint[i].D()), + cv::Point(hResult.out_LinePoint[i + 3].D(), hResult.out_LinePoint[i + 2].D()) }); + } + + if (hResult.out_Distance.Length() > 6) { + double hanYin_Width = hResult.out_Distance[0].D();//焊印的宽度 + double hanYin_Height = hResult.out_Distance[1].D();//焊印的高度 + double hanYIn_Area = hResult.out_Distance[2].D();//焊印的面积 + double jiEr_Width = hResult.out_Distance[3].D();//极耳的宽度 + double jiEr_Height = hResult.out_Distance[4].D();//极耳的高度 + double hanYin_Distance_Up = hResult.out_Distance[5].D();//焊印上边缘到极耳上边缘的距离 + double hanYin_Distance_Down = hResult.out_Distance[6].D();//焊印下边缘到极耳下边缘的距离 + + result.jiErWidth = jiEr_Width; + result.jiErHeight = jiEr_Height; + result.hanYin_Width = hanYin_Width; + result.hanYin_Height = hanYin_Height; + result.hanYin_Area = hanYIn_Area; + result.hanYin_Distance_Up = hanYin_Distance_Up; + result.hanYin_Distance_Down = hanYin_Distance_Down; + } + } + else if (hOutput.runFlag == true){//执行没崩溃,是算法内部崩溃了 + result.ngStr = gbkToUtf8(hOutput.result.out_NgDetails[0].S().Text()); + result.ngStr = result.ngStr.substr(1, result.ngStr.length() - 2); + result.result = false; + return result; + } + else { + result.ngStr = hOutput.faildResult; + result.ngStr = result.ngStr.substr(1, result.ngStr.length() - 2); + result.result = false; + return result; + } + if (result.jiErWidth == 0)result.jiErWidth = 0.001; + if (result.jiErHeight == 0)result.jiErHeight = 0.001; + if (result.hanYin_Width == 0)result.hanYin_Width = 0.001; + if (result.hanYin_Height == 0)result.hanYin_Height = 0.001; + if (result.hanYin_Area == 0)result.hanYin_Area = 0.001; + if (result.hanYin_Distance_Up == 0)result.hanYin_Distance_Up = 0.001; + if (result.hanYin_Distance_Down == 0)result.hanYin_Distance_Down = 0.001; + return result; +} + +bool hanHouCeLiangAiPart(std::shared_ptr data, yuHanHanHouParams* _detParams,cv::Mat& sourceImg, cvPainter& textPainter, cvPainter& defectPainter) { + QString groupName = "焊后顶部90度模型"; + double dingWeiSegResizeRate = data->service->_paramSyncManager->get(groupName, "极耳定位网缩小倍率", 0.1).toDouble(); + int dingWeiSegExpendSize = data->service->_paramSyncManager->get(groupName, "极耳定位后外扩大小", 10).toInt(); + int fillterWidth = data->service->_paramSyncManager->get(groupName, "缺陷过滤宽(pix)", 300).toInt(); + int fillterHeight = data->service->_paramSyncManager->get(groupName, "缺陷过滤高(pix)", 150).toInt(); + double probThreshold = data->service->_paramSyncManager->get(groupName, "缺陷CLS过滤Prob", 0.9).toDouble(); + double hanYinClsProbThreshold = data->service->_paramSyncManager->get(groupName, "焊印CLS过滤Prob", 0.98).toDouble(); + bool hanYinClsEnable = data->service->_paramSyncManager->get(groupName, "焊印CLS启用", true).toBool(); + int jiErMinWidthMax = data->service->_paramSyncManager->get(groupName, "极耳最大宽度(pix)", 1300).toInt(), + jiErMinHeightMax = data->service->_paramSyncManager->get(groupName, "极耳最大高度(pix)", 600).toInt(), + jiErMinWidthMin = data->service->_paramSyncManager->get(groupName, "极耳最小宽度(pix)", 900).toInt(), + jiErMinHeightMin = data->service->_paramSyncManager->get(groupName, "极耳最小高度(pix)", 300).toInt(); + bool jiErSizeEnable = data->service->_paramSyncManager->get(groupName, "极耳长宽判定启用", false).toBool(); + bool saveTrainLocateImg = data->service->_paramSyncManager->get(groupName, "保存训练定位网用的图片", false).toBool(); + //定位切割 + cv::Mat matForDingWei; + cv::resize(sourceImg, matForDingWei, cv::Size(), 1 * dingWeiSegResizeRate, 1 * dingWeiSegResizeRate); + cv::Mat dingWeiResultMat = ~_detParams->segYuHanDingWei->Evaluate(matForDingWei); + cv::Mat structreElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); + dilate(dingWeiResultMat, dingWeiResultMat, structreElement, cv::Point(-1, -1), 1); + cv::Rect rect = findMaxAreaContour(dingWeiResultMat); + cv::Mat matDet; + cv::Rect rectCutJier(rect.x / dingWeiSegResizeRate - dingWeiSegExpendSize, + rect.y / dingWeiSegResizeRate - dingWeiSegExpendSize, + rect.width / dingWeiSegResizeRate + dingWeiSegExpendSize * 2, + rect.height / dingWeiSegResizeRate + dingWeiSegExpendSize * 2); + sourceImg(rectCutJier & getMatRect(sourceImg)).copyTo(matDet); + //缩小检测图 + cv::resize(matDet, matDet, cv::Size(), 0.5, 0.5); + //cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertSegOriginImg(matDet); + //对定位后的极耳大小进行判断 + if (matDet.cols > jiErMinWidthMax || + matDet.cols < jiErMinWidthMin || + matDet.rows > jiErMinHeightMax || + matDet.rows < jiErMinHeightMin) { + if (matDet.cols > jiErMinWidthMax || matDet.cols < jiErMinWidthMin) + textPainter.appendPaintJob("极耳宽度过小/过大(宽" + std::to_string(matDet.cols) + ",高" + std::to_string(matDet.rows) + ")", cv::Point(75, 0), cv::Scalar(0, 0, 255)); + if (matDet.rows > jiErMinHeightMax || matDet.rows < jiErMinHeightMin) + textPainter.appendPaintJob("极耳高度过小/过大(宽" + std::to_string(matDet.cols) + ",高" + std::to_string(matDet.rows) + ")", cv::Point(75, 0), cv::Scalar(0, 0, 255)); + } + + //进行缺陷SEG检测 + std::vector> clsImgs; + cv::Mat SegRes = ~_detParams->segHanHou->Evaluate(matDet); + data->insertSegOriginImg(SegRes); + bool silieOk, fanZheOk; + handleAiProcess(matDet, SegRes, defectPainter, silieOk, fanZheOk, + cv::Point(rectCutJier.x * 0.5, rectCutJier.y * 0.5), _detParams->clsHanHou, clsImgs, + cv::Size(fillterWidth, fillterHeight), probThreshold); + data->insertClsOriginImg(clsImgs); + if (saveTrainLocateImg)//保存训练定位网用的图片 + data->insertSegOriginImg(matForDingWei); + if (silieOk == false || fanZheOk == false) + return false; + return true; +} +bool hanHouCeLiang(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_581_address address) { + QByteArray plcResult = "1"; + try { + if (data->service->_paramSyncManager->get("焊后测量", "MSA标识", false).toBool()) { + data->SNcode += "MSA_" + std::to_string(clock()); + } + auto hanyinChangLimit = genValueControlStruct("焊后测量", "焊印长", "mm", 32.0, 28.0, false, data->service->_paramSyncManager); + auto hanyinKuanLimit = genValueControlStruct("焊后测量", "焊印宽", "mm", 12.0, 8.0, false, data->service->_paramSyncManager); + auto jierChangLimit = genValueControlStruct("焊后测量", "极耳宽度", "mm", 22.0, 18.0, false, data->service->_paramSyncManager); + auto hanYin_Distance_UpLimit = genValueControlStruct("焊后测量", "焊印上边缘到极耳上边缘高度", "mm", 3.0, 0.5, false, data->service->_paramSyncManager); + auto hanYin_Distance_DownLimit = genValueControlStruct("焊后测量", "预焊印到芯包顶部间距", "mm", 10.0, 8.0, false, data->service->_paramSyncManager); + + std::vector defects; + cv::Mat sourceImg = data->getOriImg(0); + //if(data->cameraKeyName == "Camera1") + // sourceImg = cv::imread("D:/testImgs/604LF350SA01G48005284_Camera1_ORI_0_10-16-24-555.bmp", -1); + //else + // sourceImg = cv::imread("D:/testImgs/604LF350SA01G48005284_Camera3_ORI_0_10-16-25-028.bmp", -1); + //定点裁切算法用图片 + try { + int cutRowStart = data->service->_paramSyncManager->get("算法裁切", "裁切Y起点", 500).toInt(); + int cutRowHeight = data->service->_paramSyncManager->get("算法裁切", "裁切高度", sourceImg.rows - 1500).toInt(); + data->insertSegOriginImg(sourceImg(cv::Rect(0, cutRowStart, sourceImg.cols, cutRowHeight))); + sourceImg(cv::Rect(0, cutRowStart, sourceImg.cols, cutRowHeight) & getMatRect(sourceImg)).copyTo(sourceImg); + } + catch (...) { + qDebug() << QString::fromStdString(data->SNcode) << "裁切失败"; + } + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter aiTextPainter(100); + cvPainter defectPainter(50); + //auto furture = std::async(std::launch::async, hanHouCeLiangAiPart, data, _detParams, sourceImg, std::ref(aiTextPainter), std::ref(defectPainter)); + //HALCON异步处理 + cv::Mat hySegResult; + double resolution = -1; + if (data->cameraKeyName == "Camera1") + resolution = data->service->_paramSyncManager->get("像素精度", "铝顶部90度相机精度", 0.0159254).toDouble(); + else if (data->cameraKeyName == "Camera3") + resolution = data->service->_paramSyncManager->get("像素精度", "铜顶部90度相机精度", 0.0159837).toDouble(); + + bool silieOk = true, fanZheOk = true; + halconResult halconResult = halconAlgoProcess(sourceImg, _detParams->segHanYinZhuaQu, std::ref(hySegResult) + , resolution, data->service->_paramSyncManager->get("通用设置", "线体号", 1).toInt(),_detParams->evaluater); + + data->insertSegOriginImg(hySegResult); + //结果处理 + QByteArray silieResultText = silieOk == true ? "1" : "2"; + QByteArray fanzheResultText = fanZheOk == true ? "1" : "2"; + cvPainter _cvPainter(100); + if (silieOk == true && fanZheOk == true) {//OK + _cvPainter.appendPaintJob("缺陷检测OK", cv::Point(4200, 50), cv::Scalar(0, 255, 0)); + } + else { + _cvPainter.appendPaintJob("撕裂/翻折NG", cv::Point(4200, 50), cv::Scalar(0, 0, 255)); + //plcResult = "2"; + data->aiDetOK = false; + } + + silieResultText = "1"; fanzheResultText = "1"; +#pragma region 判断结果以及储存结果 + std::string typeStr = _detParams->isTong == true ? "铜" : "铝"; + if (halconResult.result == true){ + + halconResult.hanYin_Width += data->service->_paramSyncManager->get("焊后", "焊印长度补偿(mm)", 0.4).toDouble(); + valueJudgement(halconResult.jiErHeight, jierChangLimit, 5, data->halconOK, + typeStr + "极耳长度", "mm", "极耳长度NG", cv::Point(0, 125), defects, _cvPainter); + valueJudgement(halconResult.hanYin_Width, hanyinChangLimit, 10, data->halconOK, + typeStr + "焊印长度", "mm", "焊印长度NG", cv::Point(0, 225), defects, _cvPainter); + valueJudgement(halconResult.hanYin_Height, hanyinKuanLimit, 2, data->halconOK, + typeStr + "焊印宽度", "mm", "焊印宽度NG", cv::Point(0, 325), defects, _cvPainter); + + valueJudgement(halconResult.hanYin_Distance_Up, hanYin_Distance_UpLimit, 2, data->halconOK, + "焊印上边缘到极耳上边缘高度", "mm", "焊印上边缘到极耳上边缘高度NG", cv::Point(0, Paint.rows / 2 - 300), defects, _cvPainter); + valueJudgement(halconResult.hanYin_Distance_Down, hanYin_Distance_DownLimit, 3, data->halconOK, + "预焊印到芯包顶部间距", "mm", "预焊印到芯包顶部间距NG", cv::Point(0, Paint.rows / 2 - 200), defects, _cvPainter); + + if(data->halconOK == false) + plcResult = "2"; + } + else { + defects.push_back(halconResult.ngStr); + _cvPainter.appendPaintJob("算法NG", cv::Point(2100, 350), cv::Scalar(0, 0, 255)); + _cvPainter.appendPaintJob(halconResult.ngStr, cv::Point(1900, 650), cv::Scalar(0, 0, 255)); + data->halconOK = false; + plcResult = "2"; + } + + if (data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", false).toBool()) + plcResult = "1"; + + data->sqlVec.insert({ {"焊印长度",halconResult.hanYin_Width }, { "焊印宽度",halconResult.hanYin_Height }, { "极耳裁切长度",halconResult.jiErHeight } }); +#pragma endregion + //处理ai结果 + //furture.wait(); + //bool res = furture.get(); + + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + + "_" + silieResultText//发送数据-撕裂OKNG + + "," + fanzheResultText);//-翻折OKNG + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + + "_" + QByteArray::number(halconResult.jiErWidth)//发送数据-极耳宽 2 + + "," + QByteArray::number(halconResult.jiErHeight)//发送数据-极耳高 4 + + ",0" //下面会覆写 6 + + "," + QByteArray::number(halconResult.hanYin_Distance_Down) //发送数据-芯包顶部到焊印的距离 8 + + "," + QByteArray::number(halconResult.hanYin_Width)//发送数据-焊印宽 10 + + "," + QByteArray::number(halconResult.hanYin_Height));//发送数据-焊印高 12 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 6) + "_1");//发送数据-焊印位置判定结果 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 14) + "_1,1,1");//发送数据-焊印尺寸(602)/面积(581)判定结果||极耳裁切长度结果||极耳裁切宽度结果 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 100) + "_" + QByteArray::number(halconResult.hanYin_Area));//发送数据-极耳焊印面积 + //data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + // + "_" + silieResultText//发送数据-撕裂OKNG + // + "," + fanzheResultText//发送数据-翻折OKNG + //); + //data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + // + "_" + QByteArray::number(halconResult.jiErWidth)//发送数据-极耳宽 //2 + // + "," + QByteArray::number(halconResult.jiErHeight)//发送数据-极耳高 //4 + // + ",0,0"//发0 下面会覆写 + // + "," + QByteArray::number(halconResult.hanYin_Distance_Down)//发送数据-芯包顶部到焊印的距离 // 6 + // + "," + QByteArray::number(halconResult.hanYin_Width)//发送数据-极耳宽 // 8 + // + "," + QByteArray::number(halconResult.hanYin_Height)//发送数据-极耳高 // 10 + //); + //data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 6) + "_1");//发送数据-焊印位置判定结果 + //data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 14) + "_1,1,1");//发送尺寸OK + //data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + + QString typeQstr = QString::fromStdString(typeStr); + data->sqlVec.insert({ typeQstr + "算法耗时",halconResult.algoCostTime }); + data->sqlVec.insert({ typeQstr + "极耳长度",halconResult.jiErWidth }); + data->sqlVec.insert({ typeQstr + "极耳宽度",halconResult.jiErHeight }); + data->sqlVec.insert({ typeQstr + "焊印长度",halconResult.hanYin_Width }); + data->sqlVec.insert({ typeQstr + "焊印宽度",halconResult.hanYin_Height }); + data->sqlVec.insert({ typeQstr + "焊印上边缘到极耳上边缘高度",halconResult.hanYin_Distance_Up }); + data->sqlVec.insert({ typeQstr + "预焊印到芯包顶部间距",halconResult.hanYin_Distance_Down }); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + //发完结果再绘制 + for (auto& line : halconResult.lines) + cv::line(Paint, line.first, line.second, cv::Scalar(0, 255, 0), 3); + + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + aiTextPainter.executePaintJobs(Paint); + defectPainter.executePaintJobs(Paint); + _cvPainter.executePaintJobs(Paint); + putSnAndDetTime(Paint, data->SNcode, 60); + data->insertResultImg(Paint); + if (data->service->_paramSyncManager->get("焊后测量", "储存焊印定位训练用图片", false).toBool()) { + cv::Mat segTrain; + cv::resize(sourceImg, segTrain, cv::Size(), 0.2, 0.2); + data->insertSegOriginImg(segTrain); + } + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + putSnAndDetTime(Paint, data->SNcode, 60); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool() == true) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + return true; +} +bool hanHouDown45(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_581_address address) { + QByteArray plcResult = "1"; + try { + std::vector defects; + cv::Mat sourceImg = data->getOriImg(0); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + //jier定位 + float resizeRate = 10.0; + cv::Mat matForDingWei; + cv::resize(sourceImg, matForDingWei, cv::Size(), 1 / resizeRate, 1 / resizeRate); + cv::Mat dingWeiResultMat = ~_detParams->segYuHanDingWei->Evaluate(matForDingWei); + cv::Mat structreElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); + dilate(dingWeiResultMat, dingWeiResultMat, structreElement, cv::Point(-1, -1), 1); + cv::Rect rect = findMaxAreaContour(dingWeiResultMat); + + cv::Mat matDet; + cv::Rect rectCutJier(rect.x * resizeRate - 10, + rect.y * resizeRate - 10, + rect.width * resizeRate + 20, + rect.height * resizeRate + 20); + + if (data->cameraKeyName == "Camera6") { + rectCutJier = cv::Rect(1670, 1100, 2400, 900); + } + else if (data->cameraKeyName == "Camera8") { + rectCutJier = cv::Rect(970, 1100, 2400, 900); + } + else { + rectCutJier = cv::Rect(500, 500, sourceImg.cols - 1000, sourceImg.rows - 1000); + } + + sourceImg(rectCutJier & getMatRect(sourceImg)).copyTo(matDet); + + //SEG缺陷检测 + cv::resize(matDet, matDet, cv::Size(), 0.5, 0.5); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertSegOriginImg(matDet); + + cv::Mat SegRes = ~_detParams->segHanHou->Evaluate(matDet); + data->insertSegOriginImg(SegRes); + bool silieOk = true, fanZheOk = true, hanyinOk = true; + std::vector> clsImgs; + + QByteArray silieResultText = silieOk == true ? "1" : "2"; + QByteArray fanzheResultText = fanZheOk == true ? "1" : "2"; + plcResult = "1"; + + cv::Rect cutPaintRect = cv::Rect(rectCutJier.x * 0.5 - 50, rectCutJier.y * 0.5 - 50, rectCutJier.width * 0.5 + 100, rectCutJier.height * 0.5 + 100) & cv::Rect(0, 0, Paint.cols, Paint.rows); + Paint(cutPaintRect & getMatRect(Paint)).copyTo(Paint);//裁切结果图,确保界面只显示一个极耳 + + data->insertClsOriginImg(clsImgs); + + cv::Mat hanYinClsImg; + cv::resize(matDet, hanYinClsImg, cv::Size(128, 128)); + int hanYinClsRsult = 0; + double hanYinClsProb = 0; + + cvPainter _cvPainter(150); + if (silieOk == true && fanZheOk == true && hanyinOk == true) {//OK + _cvPainter.appendPaintJob("OK", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + } + else if (hanyinOk == false) { + _cvPainter.appendPaintJob("焊印NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + plcResult = "2"; + data->aiDetOK = false; + } + else { + _cvPainter.appendPaintJob("撕裂/翻折NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + plcResult = "2"; + data->aiDetOK = false; + + } + + if (plcResult == "2")defects.push_back("焊后45度NG"); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + + if (data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", false).toBool()) + plcResult = "1"; + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + + _cvPainter.executePaintJobs(Paint); + + putSnAndDetTime(Paint, data->SNcode, 55); + data->insertResultImg(Paint); + data->insertClsOriginImg({ { "hanyin_Result" + std::to_string(hanYinClsRsult) + "_" + std::to_string(hanYinClsProb),hanYinClsImg} }); + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, "异常", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; +} +bool hanHouDown90(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_581_address address) { + //获取参数 + QString groupName; + if (data->cameraKeyName == "Camera2" || data->cameraKeyName == "Camera4") + groupName = "焊后底部90度模型"; + else if (data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera7") + groupName = "焊后顶部45度模型"; + else if (data->cameraKeyName == "Camera6" || data->cameraKeyName == "Camera8") + groupName = "焊后底部45度模型"; + double dingWeiSegResizeRate = data->service->_paramSyncManager->get(groupName, "极耳定位网缩小倍率", 0.1).toDouble(); + int dingWeiSegExpendSize = data->service->_paramSyncManager->get(groupName, "极耳定位后外扩大小", 10).toInt(); + int fillterWidth = data->service->_paramSyncManager->get(groupName, "缺陷过滤宽(pix)", 300).toInt(); + int fillterHeight = data->service->_paramSyncManager->get(groupName, "缺陷过滤高(pix)", 150).toInt(); + double probThreshold = data->service->_paramSyncManager->get(groupName, "缺陷CLS过滤Prob", 0.9).toDouble(); + double hanYinClsProbThreshold = data->service->_paramSyncManager->get(groupName, "焊印CLS过滤Prob", 0.98).toDouble(); + bool hanYinClsEnable = data->service->_paramSyncManager->get(groupName, "焊印CLS启用", true).toBool(); + int jiErMinWidthMax = data->service->_paramSyncManager->get(groupName, "极耳最大宽度(pix)", 1300).toInt(), + jiErMinHeightMax = data->service->_paramSyncManager->get(groupName, "极耳最大高度(pix)", 600).toInt(), + jiErMinWidthMin = data->service->_paramSyncManager->get(groupName, "极耳最小宽度(pix)", 900).toInt(), + jiErMinHeightMin = data->service->_paramSyncManager->get(groupName, "极耳最小高度(pix)", 300).toInt(); + bool jiErSizeEnable = data->service->_paramSyncManager->get(groupName, "极耳长宽判定启用", false).toBool(); + bool saveTrainLocateImg = data->service->_paramSyncManager->get(groupName, "保存训练定位网用的图片", false).toBool(); + //初始化数据 获取图片 生产三通道结果图 + std::vector defects; + QByteArray plcResult = "1";//发送给PLC的结果 + cv::Mat sourceImg = data->getOriImg(0); + //if (data->cameraKeyName == "Camera2") + // sourceImg = cv::imread("D:/testImg/LD_NG.bmp", -1); + //else + // sourceImg = cv::imread("D:/testImg/TD_NG.bmp", -1); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + try { + bool silieOk = true, fanZheOk = true, hanyinOk = true, sizeOK = true;; + //极耳定位切割 + cv::Mat matForDingWei; + cv::resize(sourceImg, matForDingWei, cv::Size(), 1 * dingWeiSegResizeRate, 1 * dingWeiSegResizeRate); + cv::Mat dingWeiResultMat = ~_detParams->segYuHanDingWei->Evaluate(matForDingWei); + cv::Mat structreElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); + dilate(dingWeiResultMat, dingWeiResultMat, structreElement, cv::Point(-1, -1), 1); + cv::Rect rect = findMaxAreaContour(dingWeiResultMat); + cv::Mat matDet; + cv::Rect rectCutJier(rect.x / dingWeiSegResizeRate - dingWeiSegExpendSize, + rect.y / dingWeiSegResizeRate - dingWeiSegExpendSize, + rect.width / dingWeiSegResizeRate + dingWeiSegExpendSize * 2, + rect.height / dingWeiSegResizeRate + dingWeiSegExpendSize * 2); + sourceImg(rectCutJier & getMatRect(sourceImg)).copyTo(matDet); + + //缩小检测图 + cv::resize(matDet, matDet, cv::Size(), 0.5, 0.5); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertSegOriginImg(matDet); + cvPainter textPainter(75); + //对定位后的极耳大小进行判断 + if (matDet.cols > jiErMinWidthMax || + matDet.cols < jiErMinWidthMin || + matDet.rows > jiErMinHeightMax || + matDet.rows < jiErMinHeightMin) { + if (matDet.cols > jiErMinWidthMax || matDet.cols < jiErMinWidthMin) + textPainter.appendPaintJob("极耳宽度过小/过大(宽" + std::to_string(matDet.cols) + ",高" + std::to_string(matDet.rows) + ")",cv::Point(75, 0), cv::Scalar(0, 0, 255)); + if (matDet.rows > jiErMinHeightMax || matDet.rows < jiErMinHeightMin) + textPainter.appendPaintJob("极耳高度过小/过大(宽" + std::to_string(matDet.cols) + ",高" + std::to_string(matDet.rows) + ")", cv::Point(75, 0), cv::Scalar(0, 0, 255)); + if (jiErSizeEnable) {//判定启用时不进行后续检测 + defects.push_back("定位尺寸NG"); + textPainter.appendPaintJob("NG", cv::Point(0, 0), cv::Scalar(0, 0, 255)); + textPainter.executePaintJobs(Paint); + data->aiDetOK = false; + putSnAndDetTime(Paint, data->SNcode, 55); + data->insertResultImg(Paint); + if (data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + else + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + data->setNg(data->aiDetOK, data->halconOK, defects); + return false; + } + } + + //进行缺陷SEG检测 + std::vector> clsImgs; + cv::Mat SegRes = ~_detParams->segHanHou->Evaluate(matDet); + data->insertSegOriginImg(SegRes); + cvPainter defectPainter(75); + handleAiProcess(matDet, SegRes, defectPainter, silieOk, fanZheOk, + cv::Point(50, 50), _detParams->clsHanHou, clsImgs, + cv::Size(fillterWidth, fillterHeight), probThreshold); + + QByteArray silieResultText = silieOk == true ? "1" : "2"; + QByteArray fanzheResultText = fanZheOk == true ? "1" : "2"; + + cv::Rect cutPaintRect = cv::Rect(rectCutJier.x * 0.5 - 50, rectCutJier.y * 0.5 - 50, rectCutJier.width * 0.5 + 100, rectCutJier.height * 0.5 + 100) & cv::Rect(0, 0, Paint.cols, Paint.rows); + Paint(cutPaintRect & getMatRect(Paint)).copyTo(Paint);//裁切结果图,确保界面只显示一个极耳 + + data->insertClsOriginImg(clsImgs); + + + cv::Mat hanYinClsImg; + if(data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera6" || data->cameraKeyName == "Camera7" || data->cameraKeyName == "Camera8")//侧面相机 + cv::resize(matDet, hanYinClsImg, cv::Size(180, 130)); + else + cv::resize(matDet, hanYinClsImg, cv::Size(128, 128)); + clsModelWithGuard::clsResult hanYinClsRsult; + //进行焊印CLS检测 + if (_detParams->clsHanYinTest != nullptr) { + hanYinClsRsult = _detParams->clsHanYinTest->Evaluate(hanYinClsImg); + if (hanYinClsRsult.classes == 1 && hanYinClsRsult.prob > hanYinClsProbThreshold) { + defects.push_back("焊印NG"); + textPainter.appendPaintJob("焊印NG", cv::Point(50, 75), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + if (hanYinClsEnable) { + hanyinOk = false; + plcResult = "2"; + } + } + } + + if (silieOk != true || fanZheOk != true) { + if(data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera7") + defects.push_back("侧面撕裂/翻折NG"); + else + defects.push_back("底部撕裂/翻折NG"); + textPainter.appendPaintJob("撕裂/翻折NG", cv::Point(50, 150), cv::Scalar(0, 0, 255)); + plcResult = "2"; + data->aiDetOK = false; + } + if(data->aiDetOK == true && data->halconOK == true) + textPainter.appendPaintJob("OK", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + else + textPainter.appendPaintJob("NG", cv::Point(0, 0), cv::Scalar(0, 0, 255)); + if (data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", false).toBool()) + plcResult = "1"; + if (data->service->_paramSyncManager->get("通用参数","强制NG", false).toBool()) + plcResult = "2"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + //发送结果完成 进行后处理 + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + defectPainter.executePaintJobs(Paint); + textPainter.executePaintJobs(Paint); + + putSnAndDetTime(Paint, data->SNcode, 55); + data->insertResultImg(Paint); + data->insertClsOriginImg({ { "hanyin_Result" + std::to_string(hanYinClsRsult.classes) + "_" + std::to_string(hanYinClsRsult.prob),hanYinClsImg} }); + data->setNg(data->aiDetOK, data->halconOK, defects); + if (saveTrainLocateImg)//保存训练定位网用的图片 + data->insertSegOriginImg(matForDingWei); + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, "异常", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; +} + +mission* createMission() { + return new _604_YuHan_hanqian(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/yuHan/604_YuHan_hanhou/604_YuHan_hanhou.h b/src/mission/604/yuHan/604_YuHan_hanhou/604_YuHan_hanhou.h new file mode 100644 index 0000000..f96b02e --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_hanhou/604_YuHan_hanhou.h @@ -0,0 +1,192 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +struct halconInput { + HObject in_Image1; + HObject in_Image2; + HTuple in_Resolution; + HTuple in_MachineType; +}; +struct halconOutput { + HTuple out_LinePoint, out_Distance, out_Flag, out_NgDetails; +}; + +class yuHanHanHouParams { +public: + std::shared_ptr segHanYinZhuaQu = nullptr; + std::shared_ptr segYuHanDingWei = nullptr; + std::shared_ptr segHanHou = nullptr; + std::shared_ptr clsHanHou = nullptr; + std::shared_ptr clsHanYinTest = nullptr; + bool isTong = false; + + std::shared_ptr> evaluater; + yuHanHanHouParams(bool useHalcon = false) { + if (useHalcon) { + evaluater = std::make_shared>(); + evaluater->init("_1_Cal_JE_Distance", + [](std::map& inputHObj, + std::map& inputHTuple, + const halconInput& in) { + inputHObj["in_Image1"] = in.in_Image1; + inputHObj["in_Image2"] = in.in_Image2; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_MachineType"] = in.in_MachineType; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + halconOutput& out) { + out.out_LinePoint = outputHTuple["out_LinePoint"]; + out.out_Distance = outputHTuple["out_Distance"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + }); + } + } +}; +struct halconResult { + bool result = true; + double hanYin_Width = -1;//焊印宽度 + double hanYin_Height = -1;//焊印高度 + double hanYin_Area = -1;//焊印面积 + double jiErWidth = -1;//极耳的宽度 + double jiErHeight = -1;//极耳的高度 + double hanYin_Distance_Up = -1;//焊印上边缘到极耳上边缘的距离 + double hanYin_Distance_Down = -1;//焊印下边缘到极耳下边缘的距离 + std::string ngStr = ""; + cv::Mat hySegResult; + std::vector>lines; + double algoCostTime = 0; +}; + +halconResult halconAlgoProcess(cv::Mat& img, cv::Mat& Paint, handdleAI::pSegmentation* seg_hanYinZhuaQu, cv::Mat& segResult, double); +bool hanHouCeLiang(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_581_address address); +bool hanHouDown45(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_581_address address); +//bool hanHouTop45(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_604_address address); +bool hanHouDown90(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_581_address address); +class _604_YuHan_hanqian : public mission { +public: + _581_SX_Single_triggerTemplate< yuHanHanHouParams> lv_top90_1; + _581_SX_Single_triggerTemplate< yuHanHanHouParams> lv_down90_2; + _581_SX_Single_triggerTemplate< yuHanHanHouParams> tong_top90_3; + _581_SX_Single_triggerTemplate< yuHanHanHouParams> tong_down90_4; + + _581_SX_Single_triggerTemplate< yuHanHanHouParams> lv_top45_5; + _581_SX_Single_triggerTemplate< yuHanHanHouParams> lv_down45_6; + _581_SX_Single_triggerTemplate< yuHanHanHouParams> tong_top45_7; + _581_SX_Single_triggerTemplate< yuHanHanHouParams> tong_down45_8; + + //std::vector < yuHanHanHouParams> tongDetParams; + //std::vector < yuHanHanHouParams> lvDetParams; + + std::vector tong_90_DetParams; + std::vector lv_90_DetParams; + + std::vector dibu_45_tong_DetParams; + std::vector dingbu_45_tong_DetParams; + + std::vector dibu_45_lv_DetParams; + std::vector dingbu_45_lv_DetParams; + bool initOnce = false; + void onConfigReloaded() { + HDevEngineCpp::HDevEngine myEngine; + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + if (initOnce == false) { + for (int i = 0; i < 2; i++) + { + yuHanHanHouParams tong_90(i == 0); + tong_90.segYuHanDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/yuHanDingWei-90-T-seg.BHD", 16, 0, "铜Seg90度极耳定位网"); + tong_90.segHanYinZhuaQu = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanYinZhuaQu/seg-hanYinZhuaQu-T.BHD", 16, 0, "铜Seg90度焊印定位网"); + tong_90.segHanHou = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/seg-yuHanhanHou-T.BHD", 16, 0, "铜Seg90度缺陷网"); + tong_90.clsHanHou = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/cls-yuHanhanHou-T.BHD", 1, 0, "铜Cls90度缺陷网"); + tong_90.clsHanYinTest = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/cls-hanYin90_T.BHD", 1, 0, "铜90度焊印Cls网"); + tong_90.isTong = true; + tong_90_DetParams.push_back(tong_90); + + yuHanHanHouParams lv_90(i == 0); + lv_90.segYuHanDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/yuHanDingWei-90-L-seg.BHD", 16, 1, "铝Seg90度极耳定位网"); + lv_90.segHanYinZhuaQu = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanYinZhuaQu/seg-hanYinZhuaQu-L.BHD", 16, 1, "铝Seg90度焊印定位网"); + lv_90.segHanHou = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/seg-yuHanhanHou-L.BHD", 16, 1, "铝Seg90度定位网"); + lv_90.clsHanHou = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/cls-yuHanhanHou-L.BHD", 1, 1, "铝Cls90度缺陷网"); + //tong_90.clsHanYinTest = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/cls-hanYin90_L.BHD", 1, 1); + lv_90_DetParams.push_back(lv_90); + + } + + yuHanHanHouParams dingbu_45_tong; + dingbu_45_tong.segYuHanDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/yuHanDingWei-45-T-seg.BHD", 16, 0, "铜Seg45度极耳定位网"); + // //dingbu_45_tong.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuhan_hanYinZhuaQu/seg-hanYinZhuaQu-T.BHD", 16, 0); + dingbu_45_tong.segHanHou = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/yuHanhanHou-dingbu-45-T-seg.BHD", 16, 0, "铜Seg45度缺陷网"); + dingbu_45_tong.clsHanHou = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/yuHanhanHou-dingbu-45-T-cls.BHD", 1, 0, "铜Cls45度缺陷网"); + // dingbu_45_tong.clsHanYinTest = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/cls-hanYin45Top_T.BHD", 0, 1); + // dingbu_45_tong.isTong = true; + dingbu_45_tong_DetParams.push_back(dingbu_45_tong); + + yuHanHanHouParams dingbu_45_lv; + dingbu_45_lv.segYuHanDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/yuHanDingWei-45-L-seg.BHD", 16, 1, "铝Seg45度极耳定位网"); + // //dingbu_45_lv.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuhan_hanYinZhuaQu/seg-hanYinZhuaQu-L.BHD", 16, 1); + dingbu_45_lv.segHanHou = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/yuHanhanHou-dingbu-45-L-seg.BHD", 16, 1, "铝Seg45度缺陷网"); + dingbu_45_lv.clsHanHou = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/yuHanhanHou-dingbu-45-L-cls.BHD", 1, 1, "铝Cls45度缺陷网"); + // dingbu_45_lv.clsHanYinTest = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/cls-hanYin45Top_L.BHD", 1, 1); + dingbu_45_lv_DetParams.push_back(dingbu_45_lv); + + + yuHanHanHouParams dibu_45_tong; + dibu_45_tong.segYuHanDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/yuHanDingWei-90-T-seg.BHD", 16, 0, "铜Seg90度极耳定位网"); + //dibu_45_tong.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuhan_hanYinZhuaQu/seg-hanYinZhuaQu-T.BHD", 16, 0); + dibu_45_tong.segHanHou = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/seg-yuHanhanHou-T.BHD", 16, 0, "铜Seg90度缺陷网"); + dibu_45_tong.clsHanHou = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/cls-yuHanhanHou-T.BHD", 1, 0, "铜Cls90度缺陷网"); + dibu_45_tong.isTong = true; + dibu_45_tong_DetParams.push_back(dibu_45_tong); + + yuHanHanHouParams dibu_45_lv; + dibu_45_lv.segYuHanDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou90/yuHanDingWei-90-L-seg.BHD", 16, 1, "铝Seg90度极耳定位网"); + //dibu_45_lv.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuhan_hanYinZhuaQu/seg-hanYinZhuaQu-L.BHD", 16, 1); + dibu_45_lv.segHanHou = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/yuHanhanHou-dibu-45-L-seg.BHD", 16, 1, "铝Seg90度缺陷网"); + dibu_45_lv.clsHanHou = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanhou/yuhan_hanhou45/yuHanhanHou-dibu-45-L-cls.BHD", 1, 1, "铝Cls90度缺陷网"); + dibu_45_lv_DetParams.push_back(dibu_45_lv); + initOnce = true; + } + } + bool initCustom() { +#pragma region 注册触发器回调 + if (_serviceManager->_paramSyncManager->get("焊后", "hanhou", "OUT") == "OUT") { + lv_top90_1.init("Camera1", "极耳尺寸-外-铝-相机1(顶部90°)", { { 522, 1,{540},"523","530",820} }, 1, &lv_90_DetParams[0], _serviceManager, bridge, hanHouCeLiang); + lv_down90_2.init("Camera2", "极耳尺寸-外-铝-相机2(底部90°)", { { 524, 1,{540},"525","532",-1} }, 1, &lv_90_DetParams[1], _serviceManager, bridge, hanHouDown90); + tong_top90_3.init("Camera3", "极耳尺寸-外-铜-相机3(顶部90°)", { { 526,1,{540},"527","534",800} }, 1, &tong_90_DetParams[0], _serviceManager, bridge, hanHouCeLiang); + tong_down90_4.init("Camera4", "极耳尺寸-外-铜-相机4(底部90°)", { { 528, 1,{540},"529","536",-1} }, 1, &tong_90_DetParams[1], _serviceManager, bridge, hanHouDown90); + + lv_top45_5.init("Camera5", "底部撕裂-外-铝-相机5(顶部45°)", { { 562,1,{540},"563","570",-1} }, 1, &dingbu_45_lv_DetParams[0], _serviceManager, bridge, hanHouDown90); + lv_down45_6.init("Camera6", "底部撕裂-外-铝-相机6(底部45°)", { { 564, 1,{540},"565","572",-1} }, 1, &dibu_45_lv_DetParams[0], _serviceManager, bridge, hanHouDown45, "测试存图"); + tong_top45_7.init("Camera7", "底部撕裂-外-铜-相机7(顶部45°)", { { 566, 1,{540},"567","574",-1} }, 1, &dingbu_45_tong_DetParams[0], _serviceManager, bridge, hanHouDown90); + tong_down45_8.init("Camera8", "底部撕裂-外-铜-相机8(底部45°)", { { 568, 1,{540},"569","576",-1} }, 1, &dibu_45_tong_DetParams[0], _serviceManager, bridge, hanHouDown45, "测试存图"); + } + else { + lv_top90_1.init("Camera1", "极耳尺寸-内-铝-相机1(顶部90°)", { { 602, 1,{580},"603","610",860} }, 1, &lv_90_DetParams[0], _serviceManager, bridge, hanHouCeLiang); + lv_down90_2.init("Camera2", "极耳尺寸-内-铝-相机2(底部90°)", { { 604, 1,{580},"605","612",-1} }, 1, &lv_90_DetParams[1], _serviceManager, bridge, hanHouDown90); + tong_top90_3.init("Camera3", "极耳尺寸-内-铜-相机3(顶部90°)", { { 606,1,{580},"607","614",840} }, 1, &tong_90_DetParams[0], _serviceManager, bridge, hanHouCeLiang); + tong_down90_4.init("Camera4", "极耳尺寸-内-铜-相机4(底部90°)", { { 608, 1,{580},"609","616",-1} }, 1, &tong_90_DetParams[1], _serviceManager, bridge, hanHouDown90); + + lv_top45_5.init("Camera5", "底部撕裂-内-铝-相机5(顶部45°)", { { 642,1,{580},"643","650",-1} }, 1, &dingbu_45_lv_DetParams[0], _serviceManager, bridge, hanHouDown90); + lv_down45_6.init("Camera6", "底部撕裂-内-铝-相机6(底部45°)", { { 644, 1,{580},"645","652",-1} }, 1, &dibu_45_lv_DetParams[0], _serviceManager, bridge, hanHouDown45, "测试存图"); + tong_top45_7.init("Camera7", "底部撕裂-内-铜-相机7(顶部45°)", { { 646, 1,{580},"647","654",-1} }, 1, &dingbu_45_tong_DetParams[0], _serviceManager, bridge, hanHouDown90); + tong_down45_8.init("Camera8", "底部撕裂-内-铜-相机8(底部45°)", { { 648, 1,{580},"649","656",-1} }, 1, &dibu_45_tong_DetParams[0], _serviceManager, bridge, hanHouDown45, "测试存图"); + } +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/yuHan/604_YuHan_hanhou/CMakeLists.txt b/src/mission/604/yuHan/604_YuHan_hanhou/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_hanhou/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/yuHan/604_YuHan_hanhou_out.json b/src/mission/604/yuHan/604_YuHan_hanhou_out.json new file mode 100644 index 0000000..3ce8697 --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_hanhou_out.json @@ -0,0 +1,329 @@ +{ + "ImageView": { + "极耳尺寸-外-铝-相机1(顶部90°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 0, + "yPos": 0 + }, + "极耳尺寸-外-铝-相机2(底部90°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 1, + "yPos": 0 + }, + "极耳尺寸-外-铜-相机3(顶部90°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 2, + "yPos": 0 + }, + "极耳尺寸-外-铜-相机4(底部90°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 3, + "yPos": 0 + }, + "底部撕裂-外-铝-相机5(顶部45°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 0, + "yPos": 1 + }, + "底部撕裂-外-铝-相机6(底部45°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 1, + "yPos": 1 + }, + "底部撕裂-外-铜-相机7(顶部45°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 2, + "yPos": 1 + }, + "底部撕裂-外-铜-相机8(底部45°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 3, + "yPos": 1 + } + }, + "backend": { + "CameraSetting": [ + { + "connectionName": "DA8465479#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "极耳尺寸-外-铝-相机1(顶部90°)#原图" + ], + "key": "Camera1", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA7032489#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "极耳尺寸-外-铝-相机2(底部90°)#原图" + ], + "key": "Camera2", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8465440#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "极耳尺寸-外-铜-相机3(顶部90°)#原图" + ], + "key": "Camera3", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8520193#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "极耳尺寸-外-铜-相机4(底部90°)#原图" + ], + "key": "Camera4", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8465645#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "底部撕裂-外-铝-相机5(顶部45°)#原图" + ], + "key": "Camera5", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8520299#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "底部撕裂-外-铝-相机6(底部45°)#原图" + ], + "key": "Camera6", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8465751#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "底部撕裂-外-铜-相机7(顶部45°)#原图" + ], + "key": "Camera7", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8465486#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "底部撕裂-外-铜-相机8(底部45°)#原图" + ], + "key": "Camera8", + "oriSaveFormat": [ + "bmp" + ] + } + ], + "Triggers": { + "heartBeatCmd": [ + "writeHeart_short_D752_1" + ], + "initStr": "init_192.168.1.50_9600_128_6_0", + "key": "finstcp", + "moduleName": "rpc_finsTcp_boost", + "readAllCmd": [ + "read_byte_D520_800" + ], + "heartBeatIntervalMS": 800, + "sleepIntervalMS": 50 + }, + "handleAiModel": { + "cls": {}, + "det": {}, + "seg": {} + }, + "imgSaveBasePath": "D://焊后CCD存图", + "missionDllPath": "604_YuHan_hanhou", + "missionSettings": { + "hide": { + "焊后": true, + "焊后顶部45度模型": true, + "相机判定启用": true, + "通用参数": true, + "通用设置": true, + "像素精度": true, + "焊后底部90度模型": true, + "焊后测量": true, + "算法裁切": true, + "焊后底部45度模型": true, + "强制OK": true, + "本地读图": true + }, + "算法裁切": { + "裁切Y起点": 1000, + "裁切高度": 2200 + }, + "像素精度": { + "铜顶部90度相机精度": 0.015984, + "铝顶部90度相机精度": 0.015925 + }, + "焊后": { + "hanhou": "OUT", + "焊印长度补偿(mm)": -2.0 + }, + "焊后顶部45度模型": { + "极耳定位后外扩大小": 10, + "极耳定位网缩小倍率": 0.2, + "极耳最大宽度(pix)": 1000, + "极耳最大高度(pix)": 850, + "极耳最小宽度(pix)": 750, + "极耳最小高度(pix)": 350, + "极耳长宽判定启用": true, + "焊印CLS启用": true, + "焊印CLS过滤Prob": 0.98, + "缺陷CLS过滤Prob": 0.9, + "缺陷过滤宽(pix)": 300, + "缺陷过滤高(pix)": 150, + "保存训练定位网用的图片": false + }, + "焊后底部90度模型": { + "极耳定位后外扩大小": 10, + "极耳定位网缩小倍率": 0.1, + "极耳最大宽度(pix)": 1300, + "极耳最大高度(pix)": 800, + "极耳最小宽度(pix)": 900, + "极耳最小高度(pix)": 300, + "极耳长宽判定启用": true, + "焊印CLS启用": true, + "焊印CLS过滤Prob": 0.98, + "缺陷CLS过滤Prob": 0.9999, + "缺陷过滤宽(pix)": 300, + "缺陷过滤高(pix)": 150, + "保存训练定位网用的图片": false + }, + "焊后底部45度模型": { + "极耳定位网缩小倍率": 0.1, + "极耳定位后外扩大小": 10, + "缺陷过滤宽(pix)": 300, + "缺陷过滤高(pix)": 150, + "缺陷CLS过滤Prob": 0.9, + "焊印CLS过滤Prob": 0.98, + "焊印CLS启用": true, + "极耳最大宽度(pix)": 1300, + "极耳最大高度(pix)": 600, + "极耳最小宽度(pix)": 900, + "极耳最小高度(pix)": 300, + "极耳长宽判定启用": false + }, + "强制OK": { + "Camera1强制OK": false, + "Camera2强制OK": false, + "Camera3强制OK": false, + "Camera4强制OK": false, + "Camera5强制OK": false, + "Camera6强制OK": true, + "Camera7强制OK": false, + "Camera8强制OK": true + }, + "通用参数": { + "强制NG": false + }, + "通用设置": { + "存储Cls图": true, + "存储Seg图": true, + "存储原图": true, + "存储结果图": true, + "线体号": 1, + "额外存储NG图片": false, + "仅在NG时保存原图": false, + "仅在NG时保存Seg图": false, + "仅在NG时保存Cls图": false, + "仅存储NG图的内存占用上限": 70.0, + "仅存储结果图的内存占用上限": 80.0, + "不存储任何图片的内存占用上限": 90.0, + "存图汇总NG结果的等待时间(秒)": 6.0, + "启用等待NG汇总存图": false + }, + "焊后测量": { + "MSA标识": false, + "焊印长上限(mm)": 32.0, + "焊印长下限(mm)": 28.0, + "焊印长判定启用": true, + "焊印宽上限(mm)": 12.0, + "焊印宽下限(mm)": 8.0, + "焊印宽判定启用": true, + "极耳宽度上限(mm)": 22.0, + "极耳宽度下限(mm)": 18.0, + "极耳宽度判定启用": true, + "焊印上边缘到极耳上边缘高度上限(mm)": 3.0, + "焊印上边缘到极耳上边缘高度下限(mm)": 0.5, + "焊印上边缘到极耳上边缘高度判定启用": true, + "预焊印到芯包顶部间距上限(mm)": 10.0, + "预焊印到芯包顶部间距下限(mm)": 8.0, + "预焊印到芯包顶部间距判定启用": true, + "储存焊印定位训练用图片": true + }, + "本地读图": { + "多个路径用#分割,数量不足补充黑图": "", + "Camera1本地读图路径": "D:/testImgs/604LF350SA01G48005284_Camera1_ORI_0_10-16-24-555.bmp", + "Camera1启用本地读图(单次)": false, + "Camera1启用本地读图(多次)": false, + "Camera2本地读图路径": "", + "Camera2启用本地读图(单次)": false, + "Camera2启用本地读图(多次)": false, + "Camera3本地读图路径": "D:\\testImgs\\604LF350SA08G49000239_Camera3_ORI_0_16-17-57-589.bmp", + "Camera3启用本地读图(单次)": false, + "Camera3启用本地读图(多次)": false, + "Camera4本地读图路径": "", + "Camera4启用本地读图(单次)": false, + "Camera4启用本地读图(多次)": false, + "Camera5本地读图路径": "", + "Camera5启用本地读图(单次)": false, + "Camera5启用本地读图(多次)": false, + "Camera6本地读图路径": "", + "Camera6启用本地读图(单次)": false, + "Camera6启用本地读图(多次)": false, + "Camera7本地读图路径": "", + "Camera7启用本地读图(单次)": false, + "Camera7启用本地读图(多次)": false, + "Camera8本地读图路径": "", + "Camera8启用本地读图(单次)": false, + "Camera8启用本地读图(多次)": false + } + } + }, + "softwareTile": "焊后检测", + "sublUiSetting": {} +} \ No newline at end of file diff --git a/src/mission/604/yuHan/604_YuHan_hanqian/604_YuHan_hanqian.cpp b/src/mission/604/yuHan/604_YuHan_hanqian/604_YuHan_hanqian.cpp new file mode 100644 index 0000000..80e90d5 --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_hanqian/604_YuHan_hanqian.cpp @@ -0,0 +1,8 @@ +#include <604_YuHan_hanqian.h> + +mission* createMission() { + return new _604_YuHan_hanqian(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/yuHan/604_YuHan_hanqian/604_YuHan_hanqian.h b/src/mission/604/yuHan/604_YuHan_hanqian/604_YuHan_hanqian.h new file mode 100644 index 0000000..967c408 --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_hanqian/604_YuHan_hanqian.h @@ -0,0 +1,359 @@ +#include +#include +#include +#pragma region 算法 +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +class yuHanHanQianParams { +public: + std::shared_ptr segDingWei = nullptr; + + std::shared_ptr segJiErFanZhe = nullptr; + std::shared_ptr clsJiErFanZhe = nullptr; + bool isTong = true; +}; +void putSnAndDetTime(cv::Mat& Paint,const std::string& sn,int textSize = 30, const std::string& extraText = "") { + TextRendererZH textPainter(Paint.cols, Paint.rows, textSize); + textPainter.putText(Paint, "SN:" + sn + " time:" + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); +} +cv::Rect getImageLocateRect(cv::Mat& src, handdleAI::pSegmentation* seg, double bounus = 0.1) { + cv::Mat tmpImg; + cv::resize(src, tmpImg, cv::Size(), bounus, bounus); + cv::Mat ouput = ~seg->Evaluate(tmpImg); + cv::erode(ouput, ouput, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); + cv::dilate(ouput, ouput, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); + auto rect = findMaxAreaContour(ouput); + rect.x /= bounus; rect.y /= bounus; + rect.width /= bounus; rect.height /= bounus; + return rect; +} +bool handleAiProcess(cv::Mat& seg_detImg_input, cv::Mat& seg_Paint, + std::shared_ptr segPtr, std::shared_ptr clsPtr, + std::vector< std::pair >& clsImgs, double probThreshold, cv::Size fillter = cv::Size(300, 300), int ignoreY = 150,const double& afterCutImgXResizeRate = 1.0,const double& afterCutImgYResizeRate = 1.0) { + cv::Mat seg_ResultImg; + if (afterCutImgXResizeRate != 1.0 || afterCutImgYResizeRate != 1.0) { + cv::Mat tmpImg; + cv::resize(seg_detImg_input, tmpImg, cv::Size(), afterCutImgXResizeRate, afterCutImgYResizeRate); + seg_ResultImg = ~segPtr->Evaluate(tmpImg); + cv::resize(seg_ResultImg, seg_ResultImg, cv::Size(), 1 / afterCutImgXResizeRate, 1 / afterCutImgYResizeRate, cv::INTER_NEAREST); + } + else { + seg_ResultImg = ~segPtr->Evaluate(seg_detImg_input); + } + cv::erode(seg_ResultImg, seg_ResultImg, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); + cv::dilate(seg_ResultImg, seg_ResultImg, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); + std::vector> contours; + cv::findContours(seg_ResultImg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + bool finalNg = false; + TextRendererZH textPainter(seg_Paint.cols, seg_Paint.rows, 20); + clsImgs.push_back({ "seg_binary" ,seg_ResultImg }); + for (int i = 0; i < contours.size(); i++) + { + cv::Rect cls_rect = cv::boundingRect(contours[i]); + cv::Rect jugmentRect = cls_rect; + //判断是否在不检测区域 + if (cls_rect.y + cls_rect.height < ignoreY)//整个缺陷都在不检测区域 + continue; + else if (cls_rect.y < ignoreY) {//顶部部分区域在不检测范围内 + jugmentRect.height -= ignoreY - cls_rect.y; + jugmentRect.y = ignoreY; + } + if (jugmentRect.width > fillter.width && jugmentRect.height > fillter.height) { + std::vector clsImgVec = cutImg2ClsVec(cls_rect, seg_detImg_input); + bool isNg = false; + double maxNgProb = 0.0; + int ngResultNumber = 0; + for (auto& cls : clsImgVec) { + auto Result = clsPtr->Evaluate(cls); + if (Result.classes != 0 && Result.prob > probThreshold) { + ngResultNumber = Result.classes; + isNg = true; + maxNgProb = std::max(maxNgProb, Result.prob); + } + clsImgs.push_back({ "Result" + std::to_string(Result.classes) + "_prob" + std::to_string(Result.prob) ,cls }); + } + { + cv::Mat testCls; + cv::resize(seg_detImg_input(cls_rect & cv::Rect(0, 0, seg_detImg_input.cols, seg_detImg_input.rows)), testCls, cv::Size(128, 128)); + auto Result = clsPtr->Evaluate(testCls); + if (Result.classes != 0 && Result.prob > probThreshold) { + ngResultNumber = Result.classes; + isNg = true; + maxNgProb = std::max(maxNgProb, Result.prob); + } + clsImgs.push_back({ "testClsResult" + std::to_string(Result.classes) + "_prob" + std::to_string(Result.prob),testCls }); + } + if (isNg == true) { + drawContours(seg_Paint, contours, i, cv::Scalar(0, 0, 255), 3); + textPainter.putText(seg_Paint, "NGw" + std::to_string(jugmentRect.width) + + "_h" + std::to_string(jugmentRect.height) + + "_a" + std::to_string(cv::contourArea(contours[i])) + + "_r" + std::to_string(ngResultNumber) + + "_p" + std::to_string(maxNgProb) + , cv::Point(cls_rect.x, cls_rect.y), cv::Scalar(255, 255, 0)); + //cv::rectangle(seg_Paint, cls_rect, cv::Scalar(0, 255, 255), 4); + finalNg = true; + } + else { + drawContours(seg_Paint, contours, i, cv::Scalar(0, 255, 0), 3); + textPainter.putText(seg_Paint, "OKw" + std::to_string(jugmentRect.width) + + "_h" + std::to_string(jugmentRect.height) + + "_a" + std::to_string(cv::contourArea(contours[i])) + + "_r" + std::to_string(ngResultNumber) + + "_p" + std::to_string(maxNgProb) + , cv::Point(cls_rect.x, cls_rect.y), cv::Scalar(255, 255, 0)); + } + } + else if (jugmentRect.width > 100 && jugmentRect.height > 100) {//被尺寸过滤但是依旧有检测的价值 + std::vector clsImgVec = cutImg2ClsVec(cls_rect, seg_detImg_input); + double maxNgProb = 0.0; + int ngResultNumber = 0; + for (auto& cls : clsImgVec) { + auto Result = clsPtr->Evaluate(cls); + if (Result.classes != 0 && Result.prob > probThreshold) { + ngResultNumber = Result.classes; + maxNgProb = std::max(maxNgProb, Result.prob); + } + clsImgs.push_back({ "Result" + std::to_string(Result.classes) + "_prob" + std::to_string(Result.prob) ,cls }); + } + { + cv::Mat testCls; + cv::resize(seg_detImg_input(cls_rect & cv::Rect(0,0, seg_detImg_input.cols, seg_detImg_input.rows)), testCls, cv::Size(128, 128)); + auto Result = clsPtr->Evaluate(testCls); + if (Result.classes != 0 && Result.prob > probThreshold) { + ngResultNumber = Result.classes; + maxNgProb = std::max(maxNgProb, Result.prob); + } + clsImgs.push_back({ "testClsResult" + std::to_string(Result.classes) + "_prob" + std::to_string(Result.prob),testCls }); + } + drawContours(seg_Paint, contours, i, cv::Scalar(0, 255, 255, 30), 3); + textPainter.putText(seg_Paint, "尺寸过滤w" + std::to_string(jugmentRect.width) + + "_h" + std::to_string(jugmentRect.height) + + "_a" + std::to_string(cv::contourArea(contours[i])) + + "_r" + std::to_string(ngResultNumber) + + "_p" + std::to_string(maxNgProb) + , cv::Point(cls_rect.x, cls_rect.y), cv::Scalar(255, 255, 0)); + + } + + } + return finalNg; +} +QMutex batterySnMutex; +std::string battery1 = "", battery2 = ""; +bool jiErFanZhe(std::shared_ptr data, yuHanHanQianParams* _detParams, sx_581_address address) { + double dingWeiResizeRate = data->service->_paramSyncManager->get("焊前", "极耳定位网缩放比例", 0.1).toDouble(); + int jiErDingWeiExpend = data->service->_paramSyncManager->get("焊前", "极耳定位后外扩大小", 10).toInt(); + int jiErHeightMaxThreshold = data->service->_paramSyncManager->get("焊前", "极耳定位高度方向上限(pix)", 850).toInt(); + int jiErHeightMinThreshold = data->service->_paramSyncManager->get("焊前", "极耳定位高度方向下限(pix)", 350).toInt(); + bool jiErHeightEnable = data->service->_paramSyncManager->get("焊前", "极耳定位高度方向判定启用", false).toBool(); + int jiErWidthMaxThreshold = data->service->_paramSyncManager->get("焊前", "极耳定位宽度方向上限(pix)", 950).toInt(); + int jiErWidthMinThreshold = data->service->_paramSyncManager->get("焊前", "极耳定位宽度方向下限(pix)", 500).toInt(); + bool jiErWidthEnable = data->service->_paramSyncManager->get("焊前", "极耳定位宽度方向判定启用", false).toBool(); + bool sizeNgAndStillDet = data->service->_paramSyncManager->get("焊前", "极耳定位NG后仍然进行检测", false).toBool(); + bool saveDingWeiTrainImg = data->service->_paramSyncManager->get("焊前", "保存极耳定位训练用图片", true).toBool(); + double ignorePosY = 0; + double afterCutImgYResizeRate, afterCutImgXResizeRate; + if (data->cameraKeyName == "Camera2" || data->cameraKeyName == "Camera4" || data->cameraKeyName == "Camera6" || data->cameraKeyName == "Camera8") { //底部相机 + ignorePosY = data->service->_paramSyncManager->get("焊前", "底部相机不检测区域(pix)", 300).toInt(); + afterCutImgYResizeRate = data->service->_paramSyncManager->get("焊前", "底部定位切割后Y方向缩放大小", 1.0).toDouble(); + afterCutImgXResizeRate = data->service->_paramSyncManager->get("焊前", "底部定位切割后X方向缩放大小", 1.0).toDouble(); + } + else {//顶部 + ignorePosY = data->service->_paramSyncManager->get("焊前", "顶部相机不检测区域(pix)", 150).toInt(); + afterCutImgYResizeRate = data->service->_paramSyncManager->get("焊前", "顶部定位切割后Y方向缩放大小", 1.0).toDouble(); + afterCutImgXResizeRate = data->service->_paramSyncManager->get("焊前", "顶部定位切割后X方向缩放大小", 1.0).toDouble(); + } + + cv::Size fillter; + if (data->cameraKeyName == "Camera3" || data->cameraKeyName == "Camera4" || data->cameraKeyName == "Camera7" || data->cameraKeyName == "Camera8") {//铜相机 + fillter = cv::Size(data->service->_paramSyncManager->get("焊前", "铜过滤1_width", 200).toInt(), + data->service->_paramSyncManager->get("焊前", "铜过滤1_height", 200).toInt()); + } + else if (data->cameraKeyName == "Camera1" || data->cameraKeyName == "Camera2" || data->cameraKeyName == "Camera5" || data->cameraKeyName == "Camera6") {//铝相机 + fillter = cv::Size(data->service->_paramSyncManager->get("焊前", "铝过滤1_width", 200).toInt(), + data->service->_paramSyncManager->get("焊前", "铝过滤1_height", 200).toInt()); + } + double clsProb = data->service->_paramSyncManager->get("焊前", "cls置信度_prob", 0.99998).toDouble(); + qDebug() << "p1"; + try { + std::vector defects; + //存图数据加保存队列,加锁,避免数据访问冲突 + QByteArray plcResult = "1"; + cv::Mat sourceImg; + cv::resize(data->getOriImg(0), sourceImg, cv::Size(), 0.5, 0.5);//缩小进检测 + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + //定位 + cv::Mat tempLocateImg; + cv::resize(sourceImg, tempLocateImg, cv::Size(), dingWeiResizeRate * 2, dingWeiResizeRate * 2); + cv::Mat ouput = ~_detParams->segDingWei->Evaluate(tempLocateImg); + cv::erode(ouput, ouput, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); + cv::dilate(ouput, ouput, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15))); + auto rect = findMaxAreaContour(ouput); + rect.x /= dingWeiResizeRate * 2; rect.y /= dingWeiResizeRate * 2; + rect.width /= dingWeiResizeRate * 2; rect.height /= dingWeiResizeRate * 2; + qDebug() << "p2"; + //保存定位后图像 + cv::Mat seg_CutImg; + cv::Rect cutRect = rect; + cutRect.x -= jiErDingWeiExpend; cutRect.y -= jiErDingWeiExpend; + cutRect.width += jiErDingWeiExpend * 2; cutRect.height += jiErDingWeiExpend * 2; + cutRect = cutRect & (cv::Rect(0, 0, sourceImg.cols, sourceImg.rows));//防止越界 + sourceImg(cutRect).copyTo(seg_CutImg); + if (afterCutImgXResizeRate != 1.0 || afterCutImgYResizeRate != 1.0){ + cv::Mat tmpMat; + cv::resize(seg_CutImg, tmpMat, cv::Size(), afterCutImgXResizeRate, afterCutImgYResizeRate); + data->insertSegOriginImg(tmpMat); + } + else + data->insertSegOriginImg(seg_CutImg); + qDebug() << "p3"; + std::vector< std::pair > clsImgs; + TextRendererZH textPainter(Paint.cols, Paint.rows, 175); + bool dingWeiOk = true; + if (rect.height > jiErHeightMaxThreshold || rect.height < jiErHeightMinThreshold) {//定位极耳尺寸NG 高度方向 使用原始Rect判断 + if (rect.height > jiErHeightMaxThreshold) + textPainter.putText(Paint, "NG_极耳过长(高:" + std::to_string(rect.height) +"pix)", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + else + textPainter.putText(Paint, "NG_极耳过短(高:" + std::to_string(rect.height) + "pix)", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + cv::rectangle(Paint, cutRect, cv::Scalar(0, 0, 255), 4); + defects.push_back("定位尺寸NG"); + data->aiDetOK = false; + if(jiErHeightEnable) + plcResult = "2"; + dingWeiOk = false; + } + else if (rect.width > jiErWidthMaxThreshold || rect.width < jiErWidthMinThreshold) {//定位极耳尺寸NG 宽度方向 使用原始Rect判断 + if (rect.width > jiErWidthMaxThreshold) + textPainter.putText(Paint, "NG_极耳过窄(宽:" + std::to_string(rect.width) + "pix)", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + else + textPainter.putText(Paint, "NG_极耳过宽(宽:" + std::to_string(rect.width) + "pix)", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + cv::rectangle(Paint, cutRect, cv::Scalar(0, 0, 255), 4); + defects.push_back("定位尺寸NG"); + data->aiDetOK = false; + if (jiErWidthEnable) + plcResult = "2"; + dingWeiOk = false; + } + qDebug() << "p4"; + if(dingWeiOk){//定位成功 + bool isNG = handleAiProcess(seg_CutImg, Paint(cutRect), _detParams->segJiErFanZhe, _detParams->clsJiErFanZhe, clsImgs, clsProb, fillter, ignorePosY, afterCutImgXResizeRate, afterCutImgYResizeRate); + if (isNG) { + textPainter.putText(Paint, "NG_翻折/撕裂NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + defects.push_back("翻折/撕裂NG"); + plcResult = "2"; + } + + //画检测区域框 + { + cv::Rect paintRect = cutRect; + paintRect.y += ignorePosY; + paintRect.height -= ignorePosY; + cv::rectangle(Paint, paintRect, cv::Scalar(0, 255, 255), 4); + } + data->insertClsOriginImg(clsImgs); + } + qDebug() << "p5"; + if (data->aiDetOK == true)//OK + textPainter.putText(Paint, "OK", cv::Point(100, 50), cv::Scalar(0, 255, 0)); + + if (data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", true).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_" + plcResult);//结果反馈 + qDebug() << "p6"; + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + data->setNg(data->aiDetOK, data->halconOK, defects); + putSnAndDetTime(Paint, data->SNcode, 60); + data->insertResultImg(Paint); + if (saveDingWeiTrainImg) { + data->insertSegOriginImg(tempLocateImg); + data->insertSegOriginImg(ouput); + } + return true; + + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + TextRendererZH textPainter(Paint.cols, Paint.rows, 15); + textPainter.putText(Paint, "NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->halconOK = false; + data->service->_missionResultHandle->toBarChart(data->SNcode, { "检测崩溃" }); + data->setNg(data->aiDetOK, data->halconOK, { "检测崩溃" }); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool() == true) { + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_1");//结果反馈 + } + else { + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_2");//结果反馈 + } + + return true; +} +class _604_YuHan_hanqian : public mission { +public: + _581_SX_Single_triggerTemplate< yuHanHanQianParams> in_lv_top_1; + _581_SX_Single_triggerTemplate< yuHanHanQianParams> in_lv_down_2; + _581_SX_Single_triggerTemplate< yuHanHanQianParams> in_tong_top_3; + _581_SX_Single_triggerTemplate< yuHanHanQianParams> in_tong_down_4; + + _581_SX_Single_triggerTemplate< yuHanHanQianParams> out_lv_top_5; + _581_SX_Single_triggerTemplate< yuHanHanQianParams> out_lv_down_6; + _581_SX_Single_triggerTemplate< yuHanHanQianParams> out_tong_top_7; + _581_SX_Single_triggerTemplate< yuHanHanQianParams> out_tong_down_8; + std::vector < yuHanHanQianParams> tongDetParams = std::vector < yuHanHanQianParams>(4); + std::vector < yuHanHanQianParams> lvDetParams = std::vector < yuHanHanQianParams>(4); + bool initOnce = false; + void onConfigReloaded() { + if (initOnce == false) { + for (auto& params : tongDetParams) { + params.segDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanqian/hanqianTong_dingwei_seg.BHD", 16, 0, "铜Seg极耳定位网"); + params.segJiErFanZhe = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanqian/hanqian_T-seg.BHD", 16, 0, "铜Seg翻折缺陷网"); + params.clsJiErFanZhe = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanqian/hanqian_T-cls.BHD", 1, 0, "铜Cls翻折缺陷网"); + } + for (auto& params : lvDetParams) { + params.segDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanqian/hanqianLv_dingwei_seg.BHD", 16, 1, "铝Seg极耳定位网"); + params.segJiErFanZhe = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/yuhan_hanqian/hanqian_L-seg.BHD", 16, 1, "铝Seg翻折缺陷网"); + params.clsJiErFanZhe = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/yuhan_hanqian/hanqian_L-cls.BHD", 1, 1, "铝Cls翻折缺陷网"); + params.isTong = false; + } + initOnce = true; + } + } + bool initCustom() { +#pragma region 注册触发器回调 + in_lv_top_1.init("Camera1", "极耳翻折-铝-内-相机1(顶部)", { { 682, 1,{760},"683","670",780} }, 1, &lvDetParams[0], _serviceManager, bridge, jiErFanZhe); + in_lv_down_2.init("Camera2", "极耳翻折-铝-内-相机2(底部)", { { 684, 1,{760},"685","672",782} }, 1, &lvDetParams[1], _serviceManager, bridge, jiErFanZhe); + in_tong_top_3.init("Camera3", "极耳翻折-铜-内-相机3(顶部)", { { 686,1,{760},"687","674",784} }, 1, &tongDetParams[0], _serviceManager, bridge, jiErFanZhe); + in_tong_down_4.init("Camera4", "极耳翻折-铜-内-相机4(底部)", { { 688, 1,{760},"689","676",786} }, 1, &tongDetParams[1], _serviceManager, bridge, jiErFanZhe); + + out_lv_top_5.init("Camera5", "极耳翻折-铝-外-相机5(顶部)", { { 702,1,{720},"703","710",788} }, 1, &lvDetParams[2], _serviceManager, bridge, jiErFanZhe); + out_lv_down_6.init("Camera6", "极耳翻折-铝-外-相机6(底部)", { { 704, 1,{720},"705","712",790} }, 1, &lvDetParams[3], _serviceManager, bridge, jiErFanZhe); + out_tong_top_7.init("Camera7", "极耳翻折-铜-外-相机7(顶部)", { { 706, 1,{720},"707","714",792} }, 1, &tongDetParams[2], _serviceManager, bridge, jiErFanZhe); + out_tong_down_8.init("Camera8", "极耳翻折-铜-外-相机8(底部)", { { 708, 1,{720},"709","716",794} }, 1, &tongDetParams[3], _serviceManager, bridge, jiErFanZhe); +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/yuHan/604_YuHan_hanqian/CMakeLists.txt b/src/mission/604/yuHan/604_YuHan_hanqian/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_hanqian/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/yuHan/604_YuHan_jixing/604_YuHan_jixing.cpp b/src/mission/604/yuHan/604_YuHan_jixing/604_YuHan_jixing.cpp new file mode 100644 index 0000000..6a058b6 --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_jixing/604_YuHan_jixing.cpp @@ -0,0 +1,8 @@ +#include <604_YuHan_jixing.h> + +mission* createMission() { + return new _604_YuHan_hanqian(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/yuHan/604_YuHan_jixing/604_YuHan_jixing.h b/src/mission/604/yuHan/604_YuHan_jixing/604_YuHan_jixing.h new file mode 100644 index 0000000..99de181 --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_jixing/604_YuHan_jixing.h @@ -0,0 +1,137 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion + +struct jiXingHalconInput { + HObject in_Image; +}; + +struct jiXingHalconOutput { + HTuple out_ColorType; + HTuple out_Flag; +}; + +class yuHanParams { +public: + bool isTong = true; + std::shared_ptr> evaluater; + yuHanParams() { + evaluater = std::make_shared>(); + evaluater->init("YH_Color_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const jiXingHalconInput& in) { + inputHObj["in_Image"] = in.in_Image; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + jiXingHalconOutput& out) { + out.out_ColorType = outputHTuple["out_ColorType"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + } +}; +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, 0), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} + +bool halconProcess(cv::Mat& sourceImg, yuHanParams* detParams) { + jiXingHalconInput input; + input.in_Image = MatToHObject(sourceImg); + auto hOutput = detParams->evaluater->evaluate(input); + if (hOutput.runFlag != true) + return false; + + if (hOutput.result.out_Flag.D() != 1) + return false; + + return hOutput.result.out_ColorType.D() == 1;//1代表铜极OK +} +bool jiXingJianCe(std::shared_ptr data, yuHanParams* detParams, sx_581_address address) { + try{ + //存图数据加保存队列,加锁,避免数据访问冲突 + std::vectordefects; + QByteArray plcResult = "1"; + cv::Mat sourceImg; + data->getOriImg(0).copyTo(sourceImg); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + bool isOK = halconProcess(sourceImg, detParams); + + cvPainter textPainter(300); + if (isOK){//OK + textPainter.appendPaintJob("OK", cv::Point(100, 50), cv::Scalar(0, 255, 0)); + } + else + { + textPainter.appendPaintJob("NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + defects.push_back("极性错误"); + data->halconOK = false; + plcResult = "2"; + } + bool forceOk = data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", true).toBool(); + if (forceOk) + plcResult = "1";//强制OK + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + textPainter.executePaintJobs(Paint); + putSnAndDetTime(Paint, data->SNcode, 50, "强制OK:" + std::to_string(forceOk) + data->cameraKeyName); + data->insertResultImg(Paint); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; +} +class _604_YuHan_hanqian : public mission { +public: + _581_SX_Single_triggerTemplate< yuHanParams> in_lv_top_1; + _581_SX_Single_triggerTemplate< yuHanParams> in_lv_down_2; + _581_SX_Single_triggerTemplate< yuHanParams> in_tong_top_3; + _581_SX_Single_triggerTemplate< yuHanParams> in_tong_down_4; + + std::vector detParams; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + detParams = std::vector(4); + } + bool initCustom() { +#pragma region 注册触发器回调 + in_lv_top_1.init("Camera1", "极性检测-相机1", { { 400, 1,{410},"401","402",-1} }, 1, &detParams[0], _serviceManager, bridge, jiXingJianCe); + in_lv_down_2.init("Camera2", "极性检测-相机2", { { 430, 1,{440},"431","432",-1 }}, 1, &detParams[1], _serviceManager, bridge, jiXingJianCe); + in_tong_top_3.init("Camera3", "极性检测-相机3", { { 460,1,{470},"461","462",-1} }, 1, &detParams[2], _serviceManager, bridge, jiXingJianCe); + in_tong_down_4.init("Camera4", "极性检测-相机4", { { 490, 1,{500},"491","492",-1 }}, 1, &detParams[3], _serviceManager, bridge, jiXingJianCe); + +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/yuHan/604_YuHan_jixing/CMakeLists.txt b/src/mission/604/yuHan/604_YuHan_jixing/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/yuHan/604_YuHan_jixing/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/yuHan/CMakeLists.txt b/src/mission/604/yuHan/CMakeLists.txt new file mode 100644 index 0000000..f9a95fd --- /dev/null +++ b/src/mission/604/yuHan/CMakeLists.txt @@ -0,0 +1,11 @@ +# CMakeList.txt: 遍历文件夹 添加子项目 + +set(OUR_LIBS "") +SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF) +message("OUR_LIBS :" ${OUR_LIBS}) + + +# 遍历子库 +foreach(dll ${OUR_LIBS}) + add_subdirectory(${dll}) +endforeach(dll ${}) diff --git a/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/604_ZhongHan_dibutiejiao.cpp b/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/604_ZhongHan_dibutiejiao.cpp new file mode 100644 index 0000000..08a0024 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/604_ZhongHan_dibutiejiao.cpp @@ -0,0 +1,8 @@ +#include <604_ZhongHan_dibutiejiao.h> + +mission* createMission() { + return new _604_ZhongHan_dibutiejiao(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/604_ZhongHan_dibutiejiao.h b/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/604_ZhongHan_dibutiejiao.h new file mode 100644 index 0000000..c18d06a --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/604_ZhongHan_dibutiejiao.h @@ -0,0 +1,425 @@ +#include +#include +#include +#pragma region 算法 +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +struct halconInput { + HObject in_Image; + HTuple in_Product; + HTuple in_LineType; + HTuple in_Resolution; + HTuple in_FilePath; +}; +struct halconOutput { + HTuple out_Circle; + HTuple out_LinePoint; + HTuple out_Distance; + HTuple out_Rectangle; + HTuple out_NGTails; + HTuple out_Flag; +}; +struct halconResult { + bool isNG = false; + double upResult = 0.0; + double downResult = 0.0; +}; +class zhongHanFanMianTeJiaoParams { +public: + bool isTong = true; + std::string in_Product_Str = "Cu"; + std::shared_ptr seg = nullptr; + std::shared_ptr cls = nullptr; + std::shared_ptr> evaluater; + + zhongHanFanMianTeJiaoParams() { + evaluater = std::make_shared>(); + evaluater->init("_3_Neg_CoverGlue_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const halconInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Product"] = in.in_Product; + inputHTuple["in_LineType"] = in.in_LineType; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + halconOutput& out) { + out.out_Circle = outputHTuple["out_Circle"]; + out.out_LinePoint = outputHTuple["out_LinePoint"]; + out.out_Distance = outputHTuple["out_Distance"]; + out.out_Rectangle = outputHTuple["out_Rectangle"]; + out.out_NGTails = outputHTuple["out_NGTails"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + } + +}; +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn, cv::Point(0, Paint.rows - textSize * 2), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob(QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} +void GetStringSize(HDC hdc, const char* str, int* w, int* h) +{ + SIZE size; + GetTextExtentPoint32A(hdc, str, strlen(str), &size); + if (w != 0) *w = size.cx; + if (h != 0) *h = size.cy; +} +void DrawRotatedRect2Mat(cv::Mat& img, cv::RotatedRect rotateRect, cv::Scalar color, int thickness, int linType) +{ + std::vector boxPts(4); + rotateRect.points(boxPts.data()); + for (int mm = 0; mm < 4; mm++) { + cv::line(img, boxPts[mm], boxPts[(mm + 1) % 4], color, thickness, linType); + } +} +QMutex batterySnMutex; +std::string battery1 = "", battery2=""; +//画在结果图上时缩小1/2 +halconResult halconProcess(cv::Mat& sourceImg, cvPainter* textPainter, const std::string produce, const std::string& cameraKeyName, + const double& resolution, std::shared_ptr>& halconEvaluator) { + halconResult result; + try { + halconInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_Product = produce.c_str(); + hInput.in_LineType = "604"; + hInput.in_Resolution = resolution; + hInput.in_FilePath = "./halconFiles/ZH_TieJiao_Down/"; + auto hOutput = halconEvaluator->evaluate(hInput); + if (hOutput.runFlag == true && hOutput.result.out_Flag.D() == 1) { + auto& halconOutput = hOutput.result; + if (halconOutput.out_Distance.Length() > 1) { + double distance1 = halconOutput.out_Distance[0].D(); + result.upResult = distance1; + if (distance1 <= 12) {//NG + textPainter->appendPaintJob(("连接片的中心到上极耳胶纸的距离:" + QByteArray::number(distance1) + "mm").toStdString().c_str(), + cv::Point(0, 200), cv::Scalar(0, 255, 0)); + + } + else { + textPainter->appendPaintJob(("连接片的中心到上极耳胶纸的距离:" + QByteArray::number(distance1) + "mm").toStdString().c_str(), + cv::Point(0, 200), cv::Scalar(0, 255, 0)); + } + double distance2 = halconOutput.out_Distance[1].D(); + result.downResult = distance2; + if (distance2 <= 12) {//NG + textPainter->appendPaintJob(("连接片的中心到下极耳胶纸的距离:" + QByteArray::number(distance2) + "mm").toStdString().c_str(), + cv::Point(0, 400), cv::Scalar(0, 255, 0)); + } + else { + textPainter->appendPaintJob(("连接片的中心到下极耳胶纸的距离:" + QByteArray::number(distance2) + "mm").toStdString().c_str(), + cv::Point(0, 400), cv::Scalar(0, 255, 0)); + } + } + + //画圆心十字 + textPainter->appendPaintJob(cv::Point(halconOutput.out_Circle[1].D(), halconOutput.out_Circle[0].D()) / 2, 100, cv::Scalar(0, 255, 0), 5, 1); + + //画圆 + textPainter->appendPaintJob(cv::Point(halconOutput.out_Circle[1].D(), halconOutput.out_Circle[0].D()) / 2, + halconOutput.out_Circle[2].D() / 2, cv::Scalar(0, 255, 0), 5, 0); + + //画线 + if (halconOutput.out_LinePoint.Length() > 3) { + for (int i = 0; i < halconOutput.out_LinePoint.Length() / 4; i++) { + textPainter->appendPaintJob( + cv::Point(halconOutput.out_LinePoint[i * 4 + 1].D(), halconOutput.out_LinePoint[i * 4 + 0].D()) / 2, + cv::Point(halconOutput.out_LinePoint[i * 4 + 3].D(), halconOutput.out_LinePoint[i * 4 + 2].D()) / 2, + cv::Scalar(0, 255, 0), 5); + } + } + + //画矩形 + if (halconOutput.out_Rectangle.Length() > 4) { + for (int j = 0; j < halconOutput.out_Rectangle.Length() / 5; j++) { + cv::Rect rRect; + rRect.width = halconOutput.out_Rectangle[j * 5 + 3].D() / 2; + rRect.height = halconOutput.out_Rectangle[j * 5 + 4].D() / 2; + + rRect.y = halconOutput.out_Rectangle[j * 5 + 0].D() / 2 - rRect.height / 2; + rRect.x = halconOutput.out_Rectangle[j * 5 + 1].D() / 2 - rRect.width / 2; + textPainter->appendPaintJob(rRect, cv::Scalar(0, 255, 0), 3); + } + } + } + else if (hOutput.runFlag == true) { + std::string NgTails = gbkToUtf8(hOutput.result.out_NGTails.S().Text());//NG明细 + textPainter->appendPaintJob(NgTails.c_str(), cv::Point(0, 260), cv::Scalar(0, 255, 255)); + } + else { + std::string NgTails = hOutput.faildResult; + textPainter->appendPaintJob(NgTails.c_str(), cv::Point(0, 260), cv::Scalar(0, 255, 255)); + } + if (hOutput.runFlag == true && hOutput.result.out_Flag.D() == 1) { + textPainter->appendPaintJob("OK", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + result.isNG = false; + return result; + } + else { + textPainter->appendPaintJob("NG", cv::Point(0, 0), cv::Scalar(0, 0, 255)); + result.isNG = true; + return result; + } + + } + catch (HDevEngineCpp::HDevEngineException& ex) { + + std::cout << "HDevEngineException: " << ex.Message() << std::endl; \ + qDebug() << "HDevEngineException: " << ex.Message(); \ + } + catch (HalconCpp::HException& ex) { + + std::cout << "Halcon Exception Caught!" << std::endl; \ + std::cout << "Error Code: " << ex.ErrorCode() << std::endl; \ + std::cout << "Error Message: " << ex.ErrorMessage() << std::endl; \ + qDebug() << "Halcon Exception Caught!"; \ + qDebug() << "Error Code: " << ex.ErrorCode(); \ + qDebug() << "Error Message: " << ex.ErrorMessage(); \ + } + result.isNG = true; + return result; +} +bool dibutiejiao_90(std::shared_ptr data, zhongHanFanMianTeJiaoParams* _detParams, sx_581_address address) { + try{ + double 上连接片的中心到上极耳胶纸的距离上限 = data->service->_paramSyncManager->get("底部贴胶", "上连接片的中心到上极耳胶纸的距离mm(上限)", 14.0).toDouble(), + 上连接片的中心到上极耳胶纸的距离下限 = data->service->_paramSyncManager->get("底部贴胶", "上连接片的中心到上极耳胶纸的距离mm(下限)", 10.0).toDouble(), + 下连接片的中心到下极耳胶纸的距离上限 = data->service->_paramSyncManager->get("底部贴胶", "下连接片的中心到下极耳胶纸的距离mm(上限)", 14.0).toDouble(), + 下连接片的中心到下极耳胶纸的距离下限 = data->service->_paramSyncManager->get("底部贴胶", "下连接片的中心到下极耳胶纸的距离mm(下限)", 10.0).toDouble(); + bool 上连接片的中心到上极耳胶纸的距离启用 = data->service->_paramSyncManager->get("底部贴胶", "上连接片的中心到上极耳胶纸的距离启用", false).toBool(), + 下连接片的中心到下极耳胶纸的距离启用 = data->service->_paramSyncManager->get("底部贴胶", "下连接片的中心到下极耳胶纸的距离启用", false).toBool(); + double cameraResolution = data->service->_paramSyncManager->get("相机精度", QString::fromStdString(data->cameraKeyName) + "相机精度", 0.01701435).toDouble(); + //存图数据加保存队列,加锁,避免数据访问冲突 + QByteArray plcResult = "1"; + cv::Mat sourceImg = data->getOriImg(0); + //sourceImg = cv::imread("D:\\testImg\\60A01MB5601FB4N130828_60A01MB5601FB6A316208_Camera1_ORI_0_13-38-28-920.bmp", -1); + //cv::flip(sourceImg, sourceImg, 1); + std::vector defects; + cv::Mat Paint; + //if (_detParams->in_Product_Str == "Cu") { + // cv::rotate(sourceImg, sourceImg, cv::ROTATE_180);//为保证算法一致, + //} + + if (sourceImg.channels() == 1) { + cvtColor(sourceImg, Paint, cv::COLOR_GRAY2BGR); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + } + else + cv::resize(sourceImg, Paint, cv::Size(), 0.5, 0.5); + cvPainter textPainter(150); + halconResult result = halconProcess(sourceImg, &textPainter, _detParams->in_Product_Str, data->cameraKeyName, cameraResolution, _detParams->evaluater); + + if (result.isNG == true){//NG + data->halconOK = false; + plcResult = "2"; + defects.push_back("底部贴胶NG"); + } + if (!data->service->_paramSyncManager->get("底部贴胶","底部贴胶判定启用", true).toBool()) + plcResult = "1";//强制OK + + +#pragma region 发送位置判断结果 + if (data->cameraKeyName == "Camera1")data->service->_triggerManager->writeShort("D5866_" + plcResult); + else if (data->cameraKeyName == "Camera4")data->service->_triggerManager->writeShort("D5867_" + plcResult); + else if (data->cameraKeyName == "Camera7")data->service->_triggerManager->writeShort("D5868_" + plcResult); + else if (data->cameraKeyName == "Camera10")data->service->_triggerManager->writeShort("D5869_" + plcResult); +#pragma endregion + + //QByteArray sendMsg = "write_float_D" + + // QByteArray::number(address.valueOffsetAddr) + // + "_" + // + QByteArray::number(result.upResult) + // + "," + // + QByteArray::number(result.downResult) + // +"#write_short_D" + // + address.resultAddr + // +"_" + // + plcResult; + //data->service->_triggerManager->sendFinsMsg(sendMsg);//结果反馈 + data->service->_triggerManager->writeFloat("D" + + QByteArray::number(address.valueOffsetAddr) + + "_" + QByteArray::number(result.upResult) + + "," + QByteArray::number(result.downResult));//数值反馈 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult); //结果反馈 + textPainter.executePaintJobs(Paint); + putSnAndDetTime(Paint, data->SNcode, 90); + data->insertResultImg(Paint); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + //if (data->service->_paramSyncManager->get("强制OK", false).toBool()) + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //临时使用 + return true; +} +//void aiEvaluate(cv::Mat input, cv::Mat& Paint, bool& isNG) +//{ +// cv::Mat forSeg; +// input.copyTo(forSeg); +// cv::resize(forSeg, forSeg, cv::Size(), 0.25, 0.25); +// cv::Mat segRes = ~seg_diBu->Evaluate(forSeg); +// cv::resize(segRes, segRes, cv::Size(), 4, 4); +// std::vector>contours; +// cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); +// for (auto& contour : contours) { +// cv::Rect rect = cv::boundingRect(contour); +// if (rect.width < 350 || rect.height < 350) +// continue; +// cv::Mat clsImg = cutImg2Cls(rect, input); +// int claResult = cls_diBu->Evaluate(clsImg); +// switch (claResult) +// { +// case 0://OK +// cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 3); +// break; +// case 1://NG +// cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); +// putTextHusky(Paint, +// utf8_to_gbk("NG").c_str(), +// cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255), 50); +// isNG = true; +// break; +// } +// } +// +//} +bool dibutiejiao_45(std::shared_ptr data, zhongHanFanMianTeJiaoParams* _detParams, sx_581_address address) { + try { + + //存图数据加保存队列,加锁,避免数据访问冲突 + QByteArray plcResult = "2"; + cv::Mat sourceImg = data->getOriImg(0); + cv::Mat Paint; + cv::resize(sourceImg, Paint, cv::Size(), 0.5, 0.5); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + putSnAndDetTime(Paint, data->SNcode, 60); + bool isNG = false; + //aiEvaluate(sourceImg, Paint, isNG); + QString okNgSaveText = "NG"; + cvPainter textPainter(300); + if (isNG == false)//OK + { + textPainter.appendPaintJob("OK", cv::Point(100, 50), cv::Scalar(0, 255, 0)); + okNgSaveText = "OK"; + } + else + { + textPainter.appendPaintJob("NG", cv::Point(100, 50), cv::Scalar(0, 255, 0)); + data->aiDetOK = false; + plcResult = "2"; + } + textPainter.executePaintJobs(Paint); + plcResult = "1";//强制OK + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + + data->insertResultImg(Paint); + return true; + + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; +} +class _604_ZhongHan_dibutiejiao : public mission { +public: + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> chuizhi_1; + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> qingxie_2; + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> qingxie_3; + + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> chuizhi_4; + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> qingxie_5; + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> qingxie_6; + + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> chuizhi_7; + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> qingxie_8; + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> qingxie_9; + + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> chuizhi_10; + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> qingxie_11; + _581_SX_Single_triggerTemplate< zhongHanFanMianTeJiaoParams> qingxie_12; + zhongHanFanMianTeJiaoParams tongDetParams; + zhongHanFanMianTeJiaoParams lvDetParams; + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + lvDetParams.isTong = false; + tongDetParams.in_Product_Str = "Cu"; + lvDetParams.in_Product_Str="Lv"; + if (initOnce == false) { + tongDetParams.seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/seg-zhongHanHanHouDiBu.BHD", 16, 0, ""); + tongDetParams.cls = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/cls-zhongHanHanHouDiBu.BHD", 1, 0, ""); + + lvDetParams.seg = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/seg-zhongHanHanHouDiBu.BHD", 16, 1, ""); + lvDetParams.cls = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/cls-zhongHanHanHouDiBu.BHD", 1, 1, ""); + initOnce = true; + } + + } + bool initCustom() { +#pragma region 注册触发器回调 + chuizhi_1.init("Camera1", "底部贴胶检测-左-铜-相机1(90°)", { { 5500, 1,{5530,5580},"5502","5504",5506} }, 1, &tongDetParams, _serviceManager, bridge, dibutiejiao_90); + qingxie_2.init("Camera2", "底部贴胶检测-左-铜-相机2(45°)", { { 5510, 1,{5530,5580},"5512","5514",-1 } }, 1, &tongDetParams, _serviceManager, bridge, dibutiejiao_45); + qingxie_3.init("Camera3", "底部贴胶检测-左-铜-相机3(45°)", { { 5520, 1,{5530,5580},"5522","5524",-1} }, 1, &tongDetParams, _serviceManager, bridge, dibutiejiao_45); + + chuizhi_4.init("Camera4", "底部贴胶检测-左-铝-相机4(90°)", { { 5550, 1,{5530,5580},"5552","5554",5556} }, 1, &lvDetParams, _serviceManager, bridge, dibutiejiao_90); + qingxie_5.init("Camera5", "底部贴胶检测-左-铝-相机5(45°)", { { 5560, 1,{5530,5580},"5562","5564",-1} }, 1, &lvDetParams, _serviceManager, bridge, dibutiejiao_45); + qingxie_6.init("Camera6", "底部贴胶检测-左-铝-相机6(45°)", { { 5570, 1,{5530,5580},"5572","5574",-1} }, 1, &lvDetParams, _serviceManager, bridge, dibutiejiao_45); + + chuizhi_7.init("Camera7", "底部贴胶检测-右-铜-相机1(90°)", { { 5700, 1,{5730,5780},"5702","5704",5706} }, 1, &tongDetParams, _serviceManager, bridge, dibutiejiao_90); + qingxie_8.init("Camera8", "底部贴胶检测-右-铜-相机2(45°)", { { 5710, 1,{5730,5780},"5712","5714",-1} }, 1, &tongDetParams, _serviceManager, bridge, dibutiejiao_45); + qingxie_9.init("Camera9", "底部贴胶检测-右-铜-相机3(45°)", { { 5720, 1,{5730,5780},"5722","5724",-1} }, 1, &tongDetParams, _serviceManager, bridge, dibutiejiao_45); + + chuizhi_10.init("Camera10", "底部贴胶检测-右-铝-相机4(90°)", { { 5750, 1,{5730,5780},"5752","5754",5756} }, 1, &lvDetParams, _serviceManager, bridge, dibutiejiao_90); + qingxie_11.init("Camera11", "底部贴胶检测-右-铝-相机5(45°)", { { 5760, 1,{5730,5780},"5762","5764",-1} }, 1, &lvDetParams, _serviceManager, bridge, dibutiejiao_45); + qingxie_12.init("Camera12", "底部贴胶检测-右-铝-相机6(45°)", { { 5770, 1,{5730,5780},"5772","5774",-1} }, 1, &lvDetParams, _serviceManager, bridge, dibutiejiao_45); + +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/CMakeLists.txt b/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_dibutiejiao/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/zhongHan/604_ZhongHan_hanhou/604_ZhongHan_hanhou.cpp b/src/mission/604/zhongHan/604_ZhongHan_hanhou/604_ZhongHan_hanhou.cpp new file mode 100644 index 0000000..9f72c00 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_hanhou/604_ZhongHan_hanhou.cpp @@ -0,0 +1,698 @@ +#include <604_ZhongHan_hanhou.h> +cv::Mat cutLabelImg(cv::Mat pic, int W, int H, cv::Rect rect, int edgeType, int largeType, int color) +{ + cv::Point center; + center.x = rect.x + rect.width / 2; + center.y = rect.y + rect.height / 2; + + if (rect.width <= W && rect.height <= H) + { + if (edgeType == 0) + { + cv::Rect rec = { center.x - W / 2,center.y - H / 2,W,H }; + if (rec.x < 0) + rec.x = 0; + if (rec.y < 0) + rec.y = 0; + if (rec.x + rec.width > pic.cols) + rec.x = pic.cols - rec.width; + if (rec.y + rec.height > pic.rows) + rec.y = pic.rows - rec.height; + + cv::Mat picCut; + pic(rec).copyTo(picCut); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + + int x, y; + cv::Rect rec; + x = center.x - W / 2; + y = center.y - H / 2; + rec.x = x; + rec.y = y; + rec.width = W; + rec.height = H; + cv::Mat picCut(H, W, pic.type(), cv::Scalar(color, color, color)); + if (x < 0) + { + rec.width += rec.x; + xDiff = 0 - rec.x; + rec.x = 0; + } + if (y < 0) + { + rec.height += rec.y; + yDiff = 0 - rec.y; + rec.y = 0; + } + if (rec.x + rec.width > pic.cols) + rec.width = pic.cols - rec.x; + if (rec.y + rec.height > pic.rows) + rec.height = pic.rows - rec.y; + + pic(rec).copyTo(picCut({ xDiff, yDiff, rec.width, rec.height })); + return picCut; + } + } + else + { + if (largeType == 0) + { + int xDiff = 0; + int yDiff = 0; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rect.x < 0) + { + rect.width += rect.x; + rect.x = 0; + } + if (rect.y < 0) + { + rect.height += rect.y; + rect.y = 0; + } + if (rect.x + rect.width > pic.cols) + rect.width = pic.cols - rect.x; + if (rect.y + rect.height > pic.rows) + rect.height = pic.rows - rect.y; + + xDiff = (wTem - rect.width) * 1.0 / 2; + yDiff = (hTem - rect.height) * 1.0 / 2; + + pic(rect).copyTo(picCut({ xDiff, yDiff, rect.width, rect.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + else + { + int xDiff = 0; + int yDiff = 0; + cv::Rect rectTem; + float valF = rect.width * 1.0 / rect.height; + float valV = W / H; + int wTem, hTem; + if (valF <= valV) + { + hTem = rect.height; + wTem = valV * hTem; + } + else + { + wTem = rect.width; + hTem = wTem / valV; + } + rectTem = { rect.x + rect.width / 2 - wTem / 2,rect.y + rect.height / 2 - hTem / 2,wTem,hTem }; + + cv::Mat picCut(hTem, wTem, pic.type(), cv::Scalar(color, color, color)); + + if (rectTem.x < 0) + { + rectTem.width += rectTem.x; + xDiff = 0 - rectTem.x; + rectTem.x = 0; + } + if (rectTem.y < 0) + { + rectTem.height += rectTem.y; + yDiff = 0 - rectTem.y; + rectTem.y = 0; + } + if (rectTem.x + rectTem.width > pic.cols) + rectTem.width = pic.cols - rectTem.x; + if (rectTem.y + rectTem.height > pic.rows) + rectTem.height = pic.rows - rectTem.y; + + pic(rectTem).copyTo(picCut({ xDiff, yDiff, rectTem.width, rectTem.height })); + + cv::resize(picCut, picCut, cv::Size(W, H)); + return picCut; + } + } + + return cv::Mat(); +} +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn, cv::Point(0, Paint.rows - textSize * 2), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob(QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} +void exceptionResume(std::shared_ptr data, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + cvPainter textPainter(300); + textPainter.appendPaintJob(why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + +} +bool zhongHan_hanHouCeLiang(std::shared_ptr data, zhongHan2dCeLiangParams* _detParams, sx_581_address address) { + try { + auto detectStartTime = std::chrono::high_resolution_clock::now(); + +#pragma region 获取参数 + double pixAcc = data->service->_paramSyncManager->get("相机精度", QString::fromStdString(data->cameraKeyName + "相机精度"), 0.0316699).toDouble(); + double offset1 = data->service->_paramSyncManager->get("补偿", QString::fromStdString(data->cameraKeyName) + "上焊印上边缘到芯包间距补偿", 0.00).toDouble(); + double offset2 = data->service->_paramSyncManager->get("补偿", QString::fromStdString(data->cameraKeyName) + "下焊印下边缘到芯包间距补偿", 0.00).toDouble(); + double offset_HanYinSize = data->service->_paramSyncManager->get("hide", QString::fromStdString(data->cameraKeyName) + "焊印尺寸补偿", 1.50).toDouble(); + auto hanYinDingBuDaoXinBaoLimit = genValueControlStruct("焊后检测", "芯包顶部-焊印上水平边", "mm", 11.00, 9.00, false, data->service->_paramSyncManager); + //75~119上焊印下边缘到连接片圆心竖直间距 + QString typeStr = "铝"; + if (_detParams->isTong) + typeStr = "铜"; + double offsetUp = 0; + double offsetDown = 0; + valueControlStruct hanYinMianJiLimit = genValueControlStruct("焊后检测", typeStr + "焊印面积", "mm²", 119.00, 75.00, false, data->service->_paramSyncManager); + valueControlStruct hanYinChangLimit = genValueControlStruct("焊后检测", typeStr + "焊印长", "mm", 100.0, 0.0, false, data->service->_paramSyncManager); + valueControlStruct hanYinKuanLimit = genValueControlStruct("焊后检测", typeStr + "焊印长", "mm", 100.0, 0.0, false, data->service->_paramSyncManager); + valueControlStruct 上或下焊印下或上边缘到连接片圆心竖直间距 = genValueControlStruct("hide", typeStr + "(上/下)焊印(下/上)边缘到连接片圆心竖直间距", "mm", 21.0, 17.0, false, data->service->_paramSyncManager); + valueControlStruct 上焊印左或右边缘到连接片圆心水平间距 = genValueControlStruct("hide", typeStr + "上焊印左边缘到连接片圆心水平间距", "mm", 21.0, 17.0, false, data->service->_paramSyncManager); + valueControlStruct 极耳边缘到连接片上边缘距离 = genValueControlStruct("hide", typeStr + "极耳边缘到连接片上边缘距离", "mm", 5.0, 0.0, false, data->service->_paramSyncManager); + valueControlStruct 极耳边缘到焊印边缘距离 = genValueControlStruct("hide", typeStr + "极耳边缘到焊印边缘距离", "mm", 8.0, 0.0, false, data->service->_paramSyncManager); + offsetUp = data->service->_paramSyncManager->get("补偿", typeStr + "上极耳边缘补偿值", 0.0).toDouble(); + offsetDown = data->service->_paramSyncManager->get("补偿", typeStr + "下极耳边缘补偿值", 0.0).toDouble(); +#pragma endregion + std::vectordefects; + QByteArray plcResult = "1"; + + auto getParamEndTime = std::chrono::high_resolution_clock::now(); + + cv::Mat sourceImg = data->getOriImg(0); + cv::Rect cutRect; + if (_detParams->isTong == true) { + cutRect = cv::Rect(1600, 0, 3000, 3648); + } + if (_detParams->isTong == false) { + cutRect = cv::Rect(1500, 0, 3000, 3648); + } + cv::Mat cutImage = sourceImg(cutRect & (cv::Rect(0, 0, sourceImg.cols, sourceImg.rows))); + cv::Mat segProcessMat; + float resizeRate = 5.0; + cv::resize(cutImage, segProcessMat, cv::Size(), 1.0 / resizeRate, 1.0 / resizeRate); + cv::Mat segResult = _detParams->seg_hanHouDingWei->Evaluate(segProcessMat); + + auto getImageEndTime = std::chrono::high_resolution_clock::now(); + + zhongHanHanHouHalconInput hInput; + hInput.in_Image = MatToHObject(cutImage); + hInput.in_ImageSeg = MatToHObject(segResult); + hInput.in_Resolution = pixAcc; + hInput.in_FilePath = HTuple("./halconFiles/"); + hInput.in_OffsetValue = HTuple(); + hInput.in_OffsetValue.Append(offsetUp); + hInput.in_OffsetValue.Append(offsetDown); + + auto convertImageEndTime = std::chrono::high_resolution_clock::now(); + auto hOutput = _detParams->evaluater->evaluate(hInput); + HTuple out_BaseLineCor; + HTuple out_LineCor; + HTuple out_Distance1; + HTuple out_Distance2; + HTuple out_HYValues1; + HTuple out_HYValues2; + HTuple out_ColorType; + HTuple out_Flag; + HTuple out_NgTails; + HTuple out_LJP_Line; + HTuple out_DistanceUp; + HTuple out_Angle; + if (hOutput.runFlag == true) { + out_LineCor = hOutput.result.out_LineCor; + out_Distance1 = hOutput.result.out_Distance1; + out_Distance2 = hOutput.result.out_Distance2; + out_HYValues1 = hOutput.result.out_HYValues1; + out_HYValues2 = hOutput.result.out_HYValues2; + out_Flag = hOutput.result.out_Flag; + out_NgTails = hOutput.result.out_NgTails; + out_LJP_Line = hOutput.result.out_LJP_Line; + out_DistanceUp = hOutput.result.out_DistanceUp; + out_Angle = hOutput.result.out_Angle; + } + else { + out_Flag = 0; + out_NgTails = HTuple(hOutput.faildResult.c_str()); + } + auto algoExecuteEndTime = std::chrono::high_resolution_clock::now(); + + double outDistance_1_Value_0 = out_Distance1[0].D();//上焊印下边缘到连接片圆心竖直间距 + double outDistance_1_Value_1 = out_Distance1[1].D();//上焊印左/右边缘到连接片圆心水平间距 + double outDistance_2_Value_0 = out_Distance2[0].D();//下焊印上边缘到连接片圆心竖直间距 + double outDistance_2_Value_1 = out_Distance2[1].D();//下焊印左/右边缘到连接片圆心水平间距 + double hanYinDingBuDaoXinBao_Up = out_Distance1[2].D();//上焊印到芯包顶部间距 + double hanYinDingBuDaoXinBao_Down = out_Distance2[2].D();//下焊印到芯包顶部间距 + double hanYinChang_Up = out_HYValues1[0].D();//上焊印长 + double hanYinChang_Down = out_HYValues2[0].D();//下焊印长 + double hanYinKuan_Up = out_HYValues1[1].D();//上焊印宽 + double hanYinKuan_Down = out_HYValues2[1].D();//下焊印宽 + double jiErToLJPShangBianYuan_Up = out_Distance1[3].D();//上极耳到连接片上边缘 + double jiErToLJPShangBianYuan_Down = out_Distance2[3].D();//下极耳到连接片下边缘 + + cvPainter textPainter(100), textPainterLarge(300); + if (out_Flag.D() == 1) { + { + int out_LineCor_L = out_LineCor.Length(); + for (auto i = 0; i < out_LineCor_L / 4; i++) { + textPainter.appendPaintJob(cv::Point(out_LineCor[i * 4 + 1].D(), out_LineCor[i * 4].D()), + cv::Point(out_LineCor[i * 4 + 3].D(), out_LineCor[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 20); + } + } + { + int out_LJP_Line_L = out_LJP_Line.Length(); + for (auto i = 0; i < out_LJP_Line_L / 4; i++) { + textPainter.appendPaintJob(cv::Point(out_LJP_Line[i * 4].D(), out_LJP_Line[i * 4 + 1].D()), + cv::Point(out_LJP_Line[i * 4 + 2].D(), out_LJP_Line[i * 4 + 3].D()), + cv::Scalar(0, 255, 0), 20); + } + } + bool noJudgement = false; + //1 + { + valueJudgement(outDistance_1_Value_0, 上或下焊印下或上边缘到连接片圆心竖直间距, 2, data->halconOK, + "上焊印下边缘到连接片圆心竖直间距", "mm", "上焊印下边缘到连接片圆心竖直间距NG", cv::Point(0, 0), defects, textPainter); + valueJudgement(outDistance_1_Value_1, 上焊印左或右边缘到连接片圆心水平间距, 2, data->halconOK, + "上焊印左或右边缘到连接片圆心水平间距", "mm", "上焊印左或右边缘到连接片圆心水平间距NG", cv::Point(0, 100), defects, textPainter); + + //double hanYinDingBuDaoXinBao = out_Distance1[2].D() + offset1; + valueJudgement(hanYinDingBuDaoXinBao_Up, hanYinDingBuDaoXinBaoLimit, std::abs(offset1), noJudgement, + "上焊印上边缘到芯包间距", "mm", "上焊印上边缘到芯包间距NG", cv::Point(0, 200), defects, textPainter); + //double hanYinChang = out_HYValues1[0].D(); + valueJudgement(hanYinChang_Up, hanYinChangLimit, std::abs(offset_HanYinSize), noJudgement, + "上焊印长", "mm", "上焊印长NG", cv::Point(0, 300), defects, textPainter); + + //double hanYinDingKuan = out_HYValues1[1].D() + 0.3; + valueJudgement(hanYinKuan_Up, hanYinKuanLimit, std::abs(offset_HanYinSize), noJudgement, + "上焊印宽", "mm", "上焊印宽NG", cv::Point(0, 400), defects, textPainter); + + //double tongHanYinMianJi = out_HYValues1[2].D(); + //scalar = judgement(tongHanYinMianJi, hanYinMianJiLimitHigh, hanYinMianJiLimitLow, hanYinMianJiLimitEnable, plcResult); + //textPainter.putText(Paint, "上焊印面积:" + QString::number(out_HYValues1[2].D(), 'f', 3).toStdString() + "mm²", cv::Point(50, 500), scalar); + + //jiErToLJPShangBianYuan_Up = out_Distance1[3].D(); + valueJudgement(jiErToLJPShangBianYuan_Up, 极耳边缘到连接片上边缘距离, 0.0, data->halconOK, + "极耳边缘到连接片上边缘距离", "mm", "极耳边缘到连接片上边缘距离NG", cv::Point(0, 500), defects, textPainter); + + valueJudgement(out_Distance1[4].D(), 极耳边缘到焊印边缘距离, 0.0, noJudgement, + "极耳边缘到焊印边缘距离", "mm", "极耳边缘到焊印边缘距离NG", cv::Point(0, 600), defects, textPainter); + textPainter.appendPaintJob("连接片的角度:" + QString::number(out_Angle.D(), 'f', 3).toStdString() + "°", cv::Point(0, 700), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("连接片顶端到图像顶端距离:" + QString::number(out_DistanceUp[0].D(), 'f', 3).toStdString() + "mm", cv::Point(0, 800), cv::Scalar(0, 255, 0)); + } + //2 + { + textPainter.appendPaintJob("连接片下端到图像底端距离:" + QString::number(out_DistanceUp[1].D(), 'f', 3).toStdString() + "mm", cv::Point(0, cutImage.rows - 1000), cv::Scalar(0, 255, 0)); + + valueJudgement(out_Distance2[4].D(), 极耳边缘到焊印边缘距离, 0.0, noJudgement, + "极耳边缘到焊印边缘距离", "mm", "极耳边缘到焊印边缘距离NG", cv::Point(0, cutImage.rows - 900), defects, textPainter); + + //jiErToLJPShangBianYuan_Down = out_Distance2[3].D(); + //jiErToLJPShangBianYuan_Down = getPerfectValue(out_Distance2[3].D(), 极耳边缘到连接片上边缘距离下限, 极耳边缘到连接片上边缘距离上限, 0.0); + valueJudgement(jiErToLJPShangBianYuan_Down, 极耳边缘到连接片上边缘距离, 0.0, noJudgement, + "极耳边缘到连接片下边缘距离", "mm", "极耳边缘到连接片下边缘距离NG", cv::Point(0, cutImage.rows - 800), defects, textPainter); + + //double tongHanYinMianJi = out_HYValues2[2].D(); + //scalar = judgement(tongHanYinMianJi, hanYinMianJiLimitHigh, hanYinMianJiLimitLow, hanYinMianJiLimitEnable, plcResult); + //textPainter.putText(Paint, "下焊印面积:" + QString::number(out_HYValues2[2].D(), 'f', 3).toStdString() + "mm²", cv::Point(50, cutImage.rows - 650), scalar); + + //double hanYinDingKuan = out_HYValues2[1].D() + 0.3; + //hanYinKuan_Down = getPerfectValue(out_HYValues2[1].D(), hanYinKuanLimitLow, hanYinKuanLimitHigh, std::abs(offset_HanYinSize)); + valueJudgement(hanYinKuan_Down, hanYinKuanLimit, std::abs(offset_HanYinSize), noJudgement, + "下焊印宽", "mm", "下焊印宽NG", cv::Point(0, cutImage.rows - 700), defects, textPainter); + + //double hanYinChang = out_HYValues2[0].D(); + //hanYinChang_Down = getPerfectValue(out_HYValues2[0].D(), hanYinChangLimitLow, hanYinChangLimitHigh, std::abs(offset_HanYinSize)); + valueJudgement(out_HYValues2[0].D(), hanYinChangLimit, std::abs(offset_HanYinSize), noJudgement, + "下焊印长", "mm", "下焊印长NG", cv::Point(0, cutImage.rows - 600), defects, textPainter); + + //double hanYinDingBuDaoXinBao = out_Distance2[2].D() + offset2; + //hanYinDingBuDaoXinBao_Down = getPerfectValue(out_Distance2[2].D(), hanYinDingBuDaoXinBaoLimitLow, hanYinDingBuDaoXinBaoLimitHigh, std::abs(offset2)); + valueJudgement(out_Distance2[2].D(), hanYinDingBuDaoXinBaoLimit, std::abs(offset2), noJudgement, + "下焊印下边缘到芯包间距", "mm", "下焊印下边缘到芯包间距NG", cv::Point(0, cutImage.rows - 500), defects, textPainter); + + //outDistance_2_Value_1 = getPerfectValue(out_Distance2[1].D(), 上焊印左或右边缘到连接片圆心水平间距下限, 上焊印左或右边缘到连接片圆心水平间距上限, 2.0); + std::string downDistanceHorizontalText = data->cameraKeyName == "Camera1" ? "下焊印左边缘到连接片圆心水平间距" : "下焊印右边缘到连接片圆心水平间距"; + valueJudgement(outDistance_2_Value_1, 上焊印左或右边缘到连接片圆心水平间距, 2.0, noJudgement, + downDistanceHorizontalText, "mm", downDistanceHorizontalText + "NG", cv::Point(0, cutImage.rows - 400), defects, textPainter); + + //outDistance_2_Value_0 = getPerfectValue(out_Distance2[0].D(), 上或下焊印下或上边缘到连接片圆心竖直间距下限, 上或下焊印下或上边缘到连接片圆心竖直间距上限, 2.0); + valueJudgement(outDistance_2_Value_0, 上或下焊印下或上边缘到连接片圆心竖直间距, 2.0, noJudgement, + "下焊印上边缘到连接片圆心竖直间距", "mm", "下焊印上边缘到连接片圆心竖直间距NG", cv::Point(0, cutImage.rows - 300), defects, textPainter); + } + plcResult = "1";//强制OK + + if (data->cameraKeyName == "Camera1") {//铜 + data->service->_triggerManager->writeFloat("D5800_" + QByteArray::number(outDistance_1_Value_0));//A芯包铜极耳焊印到连接片中心距离(高度方向) + data->service->_triggerManager->writeFloat("D5804_" + QByteArray::number(outDistance_2_Value_0));//B芯包铜极耳焊印到连接片中心距离(高度方向) + + data->service->_triggerManager->writeFloat(\ + "D5844_" + QByteArray::number(outDistance_1_Value_1)//A芯包铜极耳焊印到连接片中心距离(宽度方向) + + "," + QByteArray::number(outDistance_2_Value_1));//B芯包铜极耳焊印到连接片中心距离(宽度方向) + + data->service->_triggerManager->writeFloat(\ + "D5820_" + QByteArray::number(hanYinDingBuDaoXinBao_Up)//A电芯芯包顶部到焊印间距(铜) + + "," + QByteArray::number(hanYinDingBuDaoXinBao_Down)//B电芯芯包顶部到焊印间距(铜) + ); + + data->service->_triggerManager->writeFloat(\ + "D5836_" + QByteArray::number(outDistance_1_Value_0)//A芯包焊印距芯包侧面(铜) + + "," + QByteArray::number(outDistance_2_Value_0));//B芯包焊印距芯包侧面(铜) + + data->service->_triggerManager->writeFloat( + "D5910_" + QByteArray::number(hanYinChang_Up)//A电芯焊印长 + + "," + QByteArray::number(hanYinKuan_Up)//A电芯焊印宽 + + "," + QByteArray::number(hanYinChang_Down)//B电芯焊印长 + + "," + QByteArray::number(hanYinKuan_Down)//B电芯焊印宽 + ); + + //A芯包铜焊印到芯包距离判定结果 + //B芯包铜焊印到芯包距离判定结果 + //A芯包铝焊印到芯包距离判定结果 + //B芯包铝焊印到芯包距离判定结果 + data->service->_triggerManager->writeShort("D5860_1,1,1,1"); + } + else {//铝 + data->service->_triggerManager->writeFloat("D5810_" + QByteArray::number(outDistance_1_Value_0));//A芯包铝极耳焊印到连接片中心距离(高度方向) + data->service->_triggerManager->writeFloat("D5814_" + QByteArray::number(outDistance_2_Value_0));//B芯包铝极耳焊印到连接片中心距离(高度方向) + + data->service->_triggerManager->writeFloat( + "D5848_" + QByteArray::number(outDistance_1_Value_1)//A芯包铝极耳焊印到连接片中心距离(宽度方向) + + "," + QByteArray::number(outDistance_2_Value_1));//B芯包铝极耳焊印到连接片中心距离(宽度方向) + + data->service->_triggerManager->writeFloat(\ + "D5828_" + QByteArray::number(hanYinDingBuDaoXinBao_Up)//A电芯芯包顶部到焊印间距(铝) + + "," + QByteArray::number(hanYinDingBuDaoXinBao_Down)//B电芯芯包顶部到焊印间距(铝) + ); + + data->service->_triggerManager->writeFloat( + "D5840_" + QByteArray::number(outDistance_1_Value_0)//A焊印侧边到芯包侧边距离 + + "," + QByteArray::number(outDistance_2_Value_0));//B焊印侧边到芯包侧边距离 + + data->service->_triggerManager->writeShort("D5861_1");//A焊印至芯包顶部间距判定结果 + data->service->_triggerManager->writeShort("D5863_1");//B焊印至芯包顶部间距判定结果 + + data->service->_triggerManager->writeFloat( + "D5918_" + QByteArray::number(hanYinChang_Up)//A电芯焊印长 + + "," + QByteArray::number(hanYinKuan_Up)//A电芯焊印宽 + + "," + QByteArray::number(hanYinChang_Down)//B电芯焊印长 + + "," + QByteArray::number(hanYinKuan_Down)//B电芯焊印宽 + ); + } + } + else { + defects.push_back("焊印测量NG"); + textPainterLarge.appendPaintJob("焊印测量NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + data->halconOK = false; + plcResult = "2"; + } + + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1";//强制OK + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + + cv::Mat Paint; + cutImage.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + textPainterLarge.executePaintJobs(Paint); + textPainter.executePaintJobs(Paint); + putSnAndDetTime(Paint, data->SNcode, 80); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->insertSegOriginImg(segProcessMat); + data->insertSegOriginImg(cutImage); + data->insertSegOriginImg(segResult); + auto detectEndTime = std::chrono::high_resolution_clock::now(); + + auto durationGetParam = std::chrono::duration_cast(getParamEndTime - detectStartTime); + auto durationGetImage = std::chrono::duration_cast(getImageEndTime - getParamEndTime); + auto durationConvertImage = std::chrono::duration_cast(convertImageEndTime - getImageEndTime); + auto durationAlgoExecuteImage = std::chrono::duration_cast(algoExecuteEndTime - convertImageEndTime); + auto durationDetectTotal = std::chrono::duration_cast(detectEndTime - detectStartTime); + + qDebug() << __FUNCTION__ << " line:" << __LINE__ \ + << "算法内部获取检测阈值参数用时:" << durationGetParam.count() << "ms"\ + << "算法内部拿到图片用时:" << durationGetImage.count() << "ms"\ + << "Mat图转HObject用时:" << durationConvertImage.count() << "ms"\ + << "halcon算法执行用时:" << durationAlgoExecuteImage.count() << "ms"\ + << "检测总用时:" << durationDetectTotal.count() << "ms"; + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "ERROR"); + return true; +} + +std::vector> aiEvaluate(cv::Mat input, cv::Mat& Paint, bool& fanZheIsNg, bool& siLieIsNg, zhongHan2dCeMianParams* _detParams) +{ + std::vector> clsImgs; + cv::Mat forSeg; + input.copyTo(forSeg); + cv::Mat segRes = ~_detParams->seg_ceMian->Evaluate(forSeg); + std::vector>contours; + cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + int clsIndex = 0; + cvPainter textPainter(50); + for (auto& contour : contours) { + clsIndex++; + cv::Rect rect = cv::boundingRect(contour); + if (rect.width < 200 || rect.height < 200) + continue; + + cv::RotatedRect rRect = cv::minAreaRect(contour); + float rWidth = rRect.size.width; + float rHeight = rRect.size.height; + if (rWidth > rHeight) { + float tempLength = rWidth; + rWidth = rHeight; + rHeight = rWidth; + } + + std::vector< cv::Mat> cutClsImages; + if (rHeight / rWidth > 10) { + cutClsImages = cutImg2ClsVec(rect, input); + } + else { + cv::Mat clsImg = cutLabelImg(input, 128, 128, rect, 1, 1, 0); + cutClsImages.push_back(clsImg); + } + + for (auto& clsImg : cutClsImages) { + auto claResult = _detParams->cls_ceMian->Evaluate(clsImg); + clsImgs.push_back({ std::to_string(clsIndex), clsImg }); + switch (claResult.classes) { + case 0://OK + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 3); + break; + case 1://NG + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + cv::drawContours(Paint, contours, clsIndex - 1, cv::Scalar(0, 0, 255), 2); + textPainter.appendPaintJob("撕裂", cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255)); + siLieIsNg = true; + break; + case 2:// + textPainter.appendPaintJob("翻折", cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255)); + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + fanZheIsNg = true; + break; + } + } + } + textPainter.executePaintJobs(Paint); + return clsImgs; +} +bool zhongHan_hanHouCeMian(std::shared_ptr data, zhongHan2dCeMianParams* _detParams, sx_581_address address) +{ + QByteArray plcResult = "1"; + + try { + bool siLieEnable = data->service->_paramSyncManager->get("焊后45°模型", "撕裂NG启用", true).toBool(); + bool fanZheEnable = data->service->_paramSyncManager->get("焊后45°模型", "翻折NG启用", true).toBool(); + double dingWeiResizeRate = data->service->_paramSyncManager->get("焊后45°模型", "极耳定位模型缩放倍率", 10.0).toDouble(); + double resizeRateAfterCut = data->service->_paramSyncManager->get("焊后45°模型", "裁切后缩放比例", 0.4).toDouble(); + cv::Mat segImage, cutImage, img2D = data->getOriImg(0); + + //img2D = cv::imread("D:/testImgs/45.bmp", -1); + if(dingWeiResizeRate != 1.0) + cv::resize(img2D, segImage, cv::Size(), 1 / dingWeiResizeRate, 1 / dingWeiResizeRate); + segImage = ~_detParams->seg_JiErDIngWei->Evaluate(segImage); + std::vector> contours; + cv::findContours(segImage, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + if (contours.size() != 0) { + int maxIndex = 0; + double maxArea = cv::contourArea(contours[0]); + for (int i = 0; i < contours.size(); i++) { + double area = cv::contourArea(contours[i]); + if (area > maxArea) { + maxIndex = i; + maxArea = area; + } + } + cv::Rect ROI = cv::boundingRect(contours[maxIndex]); + ROI.x = ROI.x * dingWeiResizeRate - 30; + ROI.y = ROI.y * dingWeiResizeRate - 30; + ROI.width = ROI.width * dingWeiResizeRate + 60; + ROI.height = ROI.height * dingWeiResizeRate + 60; + ROI = ROI & (cv::Rect(0, 0, img2D.cols, img2D.rows)); + img2D(ROI).copyTo(cutImage); + cv::resize(cutImage, cutImage, cv::Size(), resizeRateAfterCut, resizeRateAfterCut); + } + + cv::Mat Paint; + cutImage.copyTo(Paint); + putSnAndDetTime(Paint, data->SNcode, 40); + bool fanZheIsNg = false, siLieIsNg = false; + + std::vector> clsImgs; + clsImgs.resize(0); + + clsImgs = aiEvaluate(cutImage, Paint, fanZheIsNg, siLieIsNg, _detParams); + cvPainter textPainter(120); + + if (fanZheIsNg == true || siLieIsNg == true) { + std::string ngText; + if (fanZheIsNg == true)ngText += "翻折"; + if (siLieIsNg == true)ngText += "撕裂"; + textPainter.appendPaintJob(ngText + "NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + data->aiDetOK = false; + //plcResult = "1"; + } + else { + textPainter.appendPaintJob("OK", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + } + textPainter.executePaintJobs(Paint); + data->insertClsOriginImg(clsImgs); + data->insertResultImg(Paint); + data->insertSegOriginImg(cutImage); + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "ERROR"); + return true; + +} + +halconResult halconProcess(const cv::Mat& imageH, const cv::Mat& imageG, int cnt, const std::shared_ptr& ptr) +{ + halconResult hResult; + imageG.copyTo(hResult.Paint); + if (hResult.Paint.channels() == 1) + cvtColor(hResult.Paint, hResult.Paint, cv::COLOR_GRAY2BGR); + try + { + HDevEngineCpp::HDevProcedure proc("_2_FinalWelding_HY_Height"); + HDevProcedureCall procedureCall(proc); + + HObject in_ImageH, in_ImageG, in_ImageSeg; + + in_ImageH = MatToHObject(imageH); + in_ImageG = MatToHObject(imageG); + hResult.segResult = ptr->Evaluate(imageG); + in_ImageSeg = MatToHObject(hResult.segResult); + procedureCall.SetInputIconicParamObject("in_ImageH", in_ImageH); + procedureCall.SetInputIconicParamObject("in_ImageG", in_ImageG); + procedureCall.SetInputIconicParamObject("in_ImageSeg", in_ImageSeg); + + + procedureCall.SetInputCtrlParamTuple("in_xResolution", 0.004); + procedureCall.SetInputCtrlParamTuple("in_yResolution", 0.0125); + procedureCall.SetInputCtrlParamTuple("in_zResolution", 0.0008); + procedureCall.Execute();//执行halcon程序 + + HTuple out_HYNumber = procedureCall.GetOutputCtrlParamTuple("out_HYNumber");//获取输出控制类变量 + HTuple out_HYPointX = procedureCall.GetOutputCtrlParamTuple("out_HYPointX");//获取输出控制类变量 + HTuple out_HYPointY = procedureCall.GetOutputCtrlParamTuple("out_HYPointY");//获取输出控制类变量 + HTuple out_HYHeightValue = procedureCall.GetOutputCtrlParamTuple("out_HYHeightValue");//获取输出控制类变量 + HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag");//获取输出图像类变量 + HTuple out_NgDetails = procedureCall.GetOutputCtrlParamTuple("out_NgDetails");//获取输出图像类变量 + + int flag = out_Flag.D(); + cvPainter textPainter(120); + if (flag == 1) { + hResult.hyCount = (int)out_HYNumber[0].D() + (int)out_HYNumber[1].D(); + textPainter.appendPaintJob("上焊印数量:" + std::to_string((int)out_HYNumber[0].D()), cv::Point(0, 50), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("下焊印数量:" + std::to_string((int)out_HYNumber[1].D()), cv::Point(0, 150), cv::Scalar(0, 255, 0)); + + hResult.data.topHanYinCount = (int)out_HYNumber[0].D(); + hResult.data.bottomHanYinCount = (int)out_HYNumber[1].D(); + std::vector data; + for (int i = 0; i < out_HYPointX.Length() && i < out_HYPointY.Length() && i < out_HYHeightValue.Length(); i++) + { + Cross(hResult.Paint, cv::Point(out_HYPointX[i].D(), out_HYPointY[i].D()), 5, 2, cv::Scalar(0, 255, 0)); + putText(hResult.Paint, + std::to_string(out_HYHeightValue[i].D()), + cv::Point(out_HYPointX[i].D() - 30, out_HYPointY[i].D() - 15), + 1, 1, cv::Scalar(0, 255, 0), 1); + data.push_back(out_HYHeightValue[i].D()); + } + + } + else { + textPainter.appendPaintJob(out_NgDetails.ToString().Text(), cv::Point(0, 250), cv::Scalar(0, 255, 0)); + hResult.result = false; + } + textPainter.executePaintJobs(hResult.Paint); + } + CATCH_EXCEPTIONS + return hResult; +} +bool zhongHan_hanHou3D(std::shared_ptr data, zhongHan3DParams* _detParams, sx_581_address address) +{ + try + { + //data->getOriImg(0) = cv::imread("D:\\testImage\\G.tiff", -1); + //data->oriImgs[1] = cv::imread("D:\\testImage\\H.tiff", -1); + //cv::Mat imageG = data->getOriImg(0), imageH = data->getOriImg(1); + //halconResult result1 = halconProcess(imageH, imageG, 0, _detParams->seg_3dHanYinDingWei); + //data->insertSegOriginImg(result1.segResult); + + //data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_1");//结果反馈 + //data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 1) + "_1");//结果反馈 + + //cv::Mat PaintSum(result1.Paint.rows, result1.Paint.cols + result2.Paint.cols, CV_8UC3); + //result1.Paint.copyTo(PaintSum(cv::Rect(0, 0, result1.Paint.cols, result1.Paint.rows))); + //result2.Paint.copyTo(PaintSum(cv::Rect(result1.Paint.cols, 0, result1.Paint.cols, result1.Paint.rows))); + //data->insertResultImg(result1.Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; + } + CATCH_EXCEPTIONS; + exceptionResume(data, address, "ERROR"); + return true; + +} + +mission* createMission() { + return new _604_ZhongHan_hanhou(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_hanhou/604_ZhongHan_hanhou.h b/src/mission/604/zhongHan/604_ZhongHan_hanhou/604_ZhongHan_hanhou.h new file mode 100644 index 0000000..4d2344d --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_hanhou/604_ZhongHan_hanhou.h @@ -0,0 +1,171 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +struct zhongHanHanHouHalconInput { + HObject in_Image; + HObject in_ImageSeg; + HTuple in_Resolution; + HTuple in_FilePath; + HTuple in_OffsetValue; +}; + +struct zhongHanHanHouHalconOutput { + HTuple out_LineCor; + HTuple out_Distance1; + HTuple out_Distance2; + HTuple out_HYValues1; + HTuple out_HYValues2; + HTuple out_Flag; + HTuple out_NgTails; + HTuple out_LJP_Line; + HTuple out_DistanceUp; + HTuple out_Angle; +}; + +class zhongHan2dCeLiangParams { +public: + std::shared_ptr seg_hanHouDingWei = nullptr; + bool isTong = true; + std::shared_ptr> evaluater; + + zhongHan2dCeLiangParams() { + evaluater = std::make_shared>(); + evaluater->init("_2_Cal_Welding_Distance", + [](std::map& inputHObj, + std::map& inputHTuple, + const zhongHanHanHouHalconInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHObj["in_ImageSeg"] = in.in_ImageSeg; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + inputHTuple["in_OffsetValue"] = in.in_OffsetValue; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + zhongHanHanHouHalconOutput& out) { + out.out_LineCor = outputHTuple["out_LineCor"]; + out.out_Distance1 = outputHTuple["out_Distance1"]; + out.out_Distance2 = outputHTuple["out_Distance2"]; + out.out_HYValues1 = outputHTuple["out_HYValues1"]; + out.out_HYValues2 = outputHTuple["out_HYValues2"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgTails = outputHTuple["out_NgTails"]; + out.out_LJP_Line = outputHTuple["out_LJP_Line"]; + out.out_DistanceUp = outputHTuple["out_DistanceUp"]; + out.out_Angle = outputHTuple["out_Angle"]; + }); + } +}; +class zhongHan2dCeMianParams { +public: + std::shared_ptr seg_JiErDIngWei = nullptr; + std::shared_ptr seg_ceMian = nullptr; + std::shared_ptr cls_ceMian = nullptr; + bool isTong = true; +}; +class zhongHan3DParams { +public: + std::shared_ptr seg_3dHanYinDingWei = nullptr; + bool isTong = true; +}; + +struct halconResult { + struct halconData { + int topHanYinCount = 0; + int bottomHanYinCount = 0; + double avgHanYinDepth = 0; + double maxHanYinDepth = 0; + double minHanYinDepth = 0; + bool detResultIsOk = true; + }; + cv::Mat Paint; + int hyCount; + cv::Mat segResult; + bool result; + halconData data; +}; + +bool zhongHan_hanHouCeLiang(std::shared_ptr data, zhongHan2dCeLiangParams* _detParams, sx_581_address address); +std::vector> aiEvaluate(cv::Mat input, cv::Mat& Paint, bool& fanZheIsNg, bool& siLieIsNg, zhongHan2dCeMianParams* _detParams); +bool zhongHan_hanHouCeMian(std::shared_ptr data, zhongHan2dCeMianParams* _detParams, sx_581_address address); +halconResult halconProcess(const cv::Mat& imageH, const cv::Mat& imageG, int cnt, const std::shared_ptr& ptr); +bool zhongHan_hanHou3D(std::shared_ptr data, zhongHan3DParams* _detParams, sx_581_address address); + +class _604_ZhongHan_hanhou : public mission { +public: + _581_SX_Single_triggerTemplate< zhongHan2dCeLiangParams> lv_top_1; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> lv_left45_2; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> lv_right45_3; + _581_SX_Single_triggerTemplate< zhongHan3DParams> _3D_1; + + _581_SX_Single_triggerTemplate< zhongHan2dCeLiangParams> tong_top_5; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> tong_left45_6; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> tong_right45_7; + _581_SX_Single_triggerTemplate< zhongHan3DParams> _3D_2; + zhongHan2dCeLiangParams tongZhongHan2dCeLiangParams; + zhongHan2dCeLiangParams lvZhongHan2dCeLiangParams; + std::vector< zhongHan2dCeMianParams> tongZhongHan2dCeMianParams = std::vector< zhongHan2dCeMianParams>(2); + std::vector< zhongHan2dCeMianParams> lvZhongHan2dCeMianParams = std::vector< zhongHan2dCeMianParams>(2); + zhongHan3DParams tongZhongHan3DParams; + zhongHan3DParams lvZhongHan3DParams; + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + + if (initOnce == false) { + tongZhongHan2dCeLiangParams.seg_hanHouDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/hanYin/seg-hanHouDingWei-T.BHD", 16, 0, "中焊焊后90度铜定位Seg网"); + lvZhongHan2dCeLiangParams.seg_hanHouDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/hanYin/seg-hanHouDingWei-L.BHD", 16, 1, "中焊焊后90度铝定位Seg网"); + + tongZhongHan2dCeMianParams[0].seg_JiErDIngWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/pSeg_JiErDingWei_45_T_GPU0.BHD", 16, 0, "中焊焊后45度铜极耳定位Seg网"); + tongZhongHan2dCeMianParams[0].seg_ceMian = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_ZhongHan_HanHou_45_T_GPU0.BHD", 16, 0, "中焊焊后45度铜缺陷Seg网"); + tongZhongHan2dCeMianParams[0].cls_ceMian = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_ZhongHan_HanHou_45_T_GPU0.BHD", 1, 0, "中焊焊后45度铜缺陷Cls网"); + + tongZhongHan2dCeMianParams[1].seg_JiErDIngWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/pSeg_JiErDingWei_45_T_GPU0.BHD", 16, 0, "中焊焊后45度铜极耳定位Seg网"); + tongZhongHan2dCeMianParams[1].seg_ceMian = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_ZhongHan_HanHou_45_T_GPU0.BHD", 16, 0, "中焊焊后45度铜缺陷Seg网"); + tongZhongHan2dCeMianParams[1].cls_ceMian = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_ZhongHan_HanHou_45_T_GPU0.BHD", 1, 0, "中焊焊后45度铜缺陷Cls网"); + + lvZhongHan2dCeMianParams[0].seg_JiErDIngWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/pSeg_JiErDingWei_45_L_GPU1.BHD", 16, 1, "中焊焊后45度铝极耳定位Seg网"); + lvZhongHan2dCeMianParams[0].seg_ceMian = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_ZhongHan_HanHou_45_L_GPU1.BHD", 16, 1, "中焊焊后45度铝缺陷Seg网"); + lvZhongHan2dCeMianParams[0].cls_ceMian = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_ZhongHan_HanHou_45_L_GPU1.BHD", 1, 1, "中焊焊后45度铝缺陷Cls网"); + + lvZhongHan2dCeMianParams[1].seg_JiErDIngWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/pSeg_JiErDingWei_45_L_GPU1.BHD", 16, 1, "中焊焊后45度铝极耳定位Seg网"); + lvZhongHan2dCeMianParams[1].seg_ceMian = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_ZhongHan_HanHou_45_L_GPU1.BHD", 16, 1, "中焊焊后45度铝缺陷Seg网"); + lvZhongHan2dCeMianParams[1].cls_ceMian = _serviceManager->_handleAiManager->loadClsModel("./handleAiModels/cls_ZhongHan_HanHou_45_L_GPU1.BHD", 1, 1, "中焊焊后45度铝缺陷Cls网"); + + lvZhongHan2dCeLiangParams.isTong = false; + + tongZhongHan3DParams.seg_3dHanYinDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_3dHanYin.BHD", 16, 0, "中焊焊后3D铜Seg网"); + lvZhongHan3DParams.seg_3dHanYinDingWei = _serviceManager->_handleAiManager->loadSegModel("./handleAiModels/seg_3dHanYin.BHD", 16, 1, "中焊焊后3D铝Seg网"); + + initOnce = true; + } + } + bool initCustom() { +#pragma region 注册触发器回调 + lv_top_1.init("Camera1", "焊印检测-内-铜-相机1(90°)", { { 5360, 1,{5340,5420},"5362","5364",5800} }, 1, &tongZhongHan2dCeLiangParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); + lv_left45_2.init("Camera2", "翻折撕裂-内-铜-相机2(45°)", { { 5370, 1,{5340,5420},"5372","5374",-1} }, 1, &tongZhongHan2dCeMianParams[0], _serviceManager, bridge, zhongHan_hanHouCeMian); + lv_right45_3.init("Camera3", "翻折撕裂-内-铜-相机3(45°)", { { 5380,1,{5340,5420},"5382","5384",5406} }, 1, &tongZhongHan2dCeMianParams[1], _serviceManager, bridge, zhongHan_hanHouCeMian); + _3D_1.init("Camera4", "焊后检测-3D相机2(右)" , { { 5400, 1,{5340,5420},"5402","5404",-1} }, 1, &tongZhongHan3DParams, _serviceManager, bridge, zhongHan_hanHou3D); + + tong_top_5.init("Camera5", "焊印检测-外-铝-相机5(90°)", { { 5440, 1,{5340,5420},"5442","5444",5804} }, 1, &lvZhongHan2dCeLiangParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); + tong_left45_6.init("Camera6", "翻折撕裂-外-铝-相机6(45°)", { { 5450,1,{5340,5420},"5452","5454",-1} }, 1, &lvZhongHan2dCeMianParams[0], _serviceManager, bridge, zhongHan_hanHouCeMian); + tong_right45_7.init("Camera7", "翻折撕裂-外-铝-相机7(45°)", { { 5460, 1,{5340,5420},"5462","5464",5486} }, 1, &lvZhongHan2dCeMianParams[1], _serviceManager, bridge, zhongHan_hanHouCeMian); + _3D_2.init("Camera8", "焊后检测-3D相机1(左)", { { 5480, 1,{5340,5420},"5482","5484",-1} }, 1, &lvZhongHan3DParams, _serviceManager, bridge, zhongHan_hanHou3D); +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_hanhou/CMakeLists.txt b/src/mission/604/zhongHan/604_ZhongHan_hanhou/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_hanhou/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/604_ZhongHan_tiejiao_dingbu.cpp b/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/604_ZhongHan_tiejiao_dingbu.cpp new file mode 100644 index 0000000..068182a --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/604_ZhongHan_tiejiao_dingbu.cpp @@ -0,0 +1,573 @@ +#include <604_ZhongHan_tiejiao_dingbu.h> +#include +#include +#include +#include +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, const std::string& extraText = "") { + int textSize = 180 * (Paint.rows / 2736); + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn, cv::Point(0, Paint.rows - textSize * 2), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob(QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} +bool TieJiaoZhengMian90(std::shared_ptr data, ZhongHanTieJiaoParams* _detParams, sx_581_address address) { + try + { + double pixAccTong = data->service->_paramSyncManager->get("相机精度", "顶部贴胶检测铜极相机精度", 0.02292).toDouble(); + double pixAccLv = data->service->_paramSyncManager->get("相机精度", "顶部贴胶检测铝极相机精度", 0.02292).toDouble(); + auto 上连接片的中心到上极耳胶纸的距离 = genValueControlStruct("顶部贴胶", "上连接片的中心到上极耳胶纸的距离", "mm", 14.0, 10.0, false, data->service->_paramSyncManager); + auto 下连接片的中心到下极耳胶纸的距离 = genValueControlStruct("顶部贴胶", "下连接片的中心到下极耳胶纸的距离", "mm", 14.0, 10.0, false, data->service->_paramSyncManager); + //存图数据加保存队列,加锁,避免数据访问冲突 + QByteArray plcResult = "1"; + cv::Mat sourceImg = data->getOriImg(0); + cv::Mat Paint; + std::vectordefects; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + double pixAcc = 0; + if (_detParams->in_Product_Str == "Cu")pixAcc = pixAccTong; + else pixAcc = pixAccLv; + + tiejiaoHalconInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_Product = HalconCpp::HTuple((char*)(_detParams->in_Product_Str).c_str());//产品类别, 'Cu':铜 'Al':铝 + hInput.in_LineType = "604";//线体类别 + hInput.in_Resolution = pixAcc;//相机精度(mm/pix) + hInput.in_FilePath = HalconCpp::HTuple(std::string("./halconFiles/ZH_TieJiao_Up/" + data->cameraKeyName + "/").c_str());//模板文件路径 + + auto hOutput = _detParams->evaluater->evaluate(hInput); + HalconCpp::HTuple out_Circle, out_LinePoint, out_Rectangle, out_NGTails, out_Distance, out_Flag; + if (hOutput.runFlag == true) { + out_Circle = hOutput.result.out_Circle; + out_LinePoint = hOutput.result.out_LinePoint; + out_Rectangle = hOutput.result.out_Rectangle; + out_NGTails = hOutput.result.out_NGTails; + out_Distance = hOutput.result.out_Distance; + out_Flag = hOutput.result.out_Flag; + } + else { + out_Flag = 0; + out_NGTails = HalconCpp::HTuple(hOutput.faildResult.c_str()); + } + cvPainter textPainter(300); + if (out_Flag.D() == 1) { + //画圆心十字 + textPainter.appendPaintJob(cv::Point(out_Circle[1].D(), out_Circle[0].D()), 200, cv::Scalar(0, 255, 0), 5,1); + + //画圆 + textPainter.appendPaintJob(cv::Point(out_Circle[1].D(), out_Circle[0].D()), + out_Circle[2].D(), cv::Scalar(0, 255, 0), 5 ,0); + + //画线 + if (out_LinePoint.Length() > 3) { + for (int i = 0; i < out_LinePoint.Length() / 4; i++) { + textPainter.appendPaintJob( + cv::Point(out_LinePoint[i * 4 + 1].D(), out_LinePoint[i * 4 + 0].D()), + cv::Point(out_LinePoint[i * 4 + 3].D(), out_LinePoint[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 5); + } + } + + //画矩形 + if (out_Rectangle.Length() > 4) { + for (int j = 0; j < out_Rectangle.Length() / 5; j++) { + cv::RotatedRect rRect; + rRect.center.y = out_Rectangle[j * 5 + 0].D(); + rRect.center.x = out_Rectangle[j * 5 + 1].D(); + rRect.angle = out_Rectangle[j * 5 + 2].D(); + rRect.size.width = out_Rectangle[j * 5 + 3].D(); + rRect.size.height = out_Rectangle[j * 5 + 4].D(); + + DrawRotatedRect2Mat(Paint, rRect, cv::Scalar(0, 255, 0), 3, cv::LINE_4); + } + } + + double distance1 = out_Distance[0].D(); + valueJudgement(distance1, 上连接片的中心到上极耳胶纸的距离, 0.0, data->halconOK, + "连接片的中心到上极耳胶纸的距离:", "mm", "连接片的中心到上极耳胶纸的距离NG", cv::Point(0, 400), defects, textPainter); + data->sqlVec.insert({ (_detParams->in_Product_Str == "Cu" ? "铜连接片的中心到上极耳胶纸的距离" : "铝连接片的中心到上极耳胶纸的距离") ,distance1 }); + + double distance2 = out_Distance[1].D(); + valueJudgement(distance2, 下连接片的中心到下极耳胶纸的距离, 0.0, data->halconOK, + "连接片的中心到下极耳胶纸的距离:", "mm", "连接片的中心到下极耳胶纸的距离NG", cv::Point(0, 800), defects, textPainter); + data->sqlVec.insert({ (_detParams->in_Product_Str == "Cu" ? "铜连接片的中心到下极耳胶纸的距离" : "铝连接片的中心到下极耳胶纸的距离"),distance2 }); + if (data->halconOK == false) + plcResult = "2"; + + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(distance1) + "," + QByteArray::number(distance2)); + if (plcResult == "1") { + textPainter.appendPaintJob("OK", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + } + else { + textPainter.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + } + } + else { + textPainter.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + plcResult = "2"; + data->halconOK = false; + std::string NgTails = gbkToUtf8(out_NGTails[0].S().Text());//NG明细 + textPainter.appendPaintJob(NgTails, cv::Point(0, 600), cv::Scalar(0, 255, 255)); + defects.push_back(NgTails); + } + + if (data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", false).toBool()) + plcResult = "1"; + + if (data->cameraKeyName == "Camera1")data->service->_triggerManager->writeShort("D5864_" + plcResult); + else if (data->cameraKeyName == "Camera4")data->service->_triggerManager->writeShort("D5865_" + plcResult); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + putSnAndDetTime(Paint, data->SNcode); + textPainter.executePaintJobs(Paint); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->setNg(data->aiDetOK, data->halconOK, defects); + return true; + } + CATCH_EXCEPTIONS; + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //临时使用 + return true; +} + +bool TieJiaoZhengMian45(std::shared_ptr data, ZhongHanTieJiaoParams* _detParams, sx_581_address address) { + try + { + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + putSnAndDetTime(Paint, data->SNcode); + bool isNG = false; + QByteArray plcResult = "1"; + cvPainter textainter(300); + if (isNG == true) + { + plcResult = "2"; + textainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + } + else { + textainter.appendPaintJob("OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + textainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5);//结果图缩放 + data->insertResultImg(Paint); + + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + return true; + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) { + cv::cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + } + QByteArray plcResult = "2"; + cvPainter textainter(300); + textainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + textainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + return true; +} + +void DrawRotatedRect2Mat(cv::Mat& img, cv::RotatedRect rotateRect, cv::Scalar color, int thickness, int linType) +{ + std::vector boxPts(4); + rotateRect.points(boxPts.data()); + for (int mm = 0; mm < 4; mm++) { + cv::line(img, boxPts[mm], boxPts[(mm + 1) % 4], color, thickness, linType); + } +} + + +void HanQianputSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} +bool hanQianCeLiang_Main(std::shared_ptr data, zhongHanHanQianParams* _detParams, sx_581_address address) { + try { + std::vectordefects; + QByteArray plcResult = "1"; + double pixAccTong = data->service->_paramSyncManager->get("相机精度", "焊前检测铜极相机精度", 0.35336).toDouble(); + double pixAccLv = data->service->_paramSyncManager->get("相机精度", "焊前检测铝极相机精度", 0.35083).toDouble(); + auto xinBaoJianJuLimit = genValueControlStruct("焊前检测", "芯包间距", "mm", 72.00, 68.00, false, data->service->_paramSyncManager); + auto lianJiePianJianJuLimit = genValueControlStruct("焊前检测", "连接片间距", "mm", 124.00, 122.00, false, data->service->_paramSyncManager); + auto 铜极连接片中心的和上极耳电芯边缘距离 = genValueControlStruct("焊前检测", "铜极连接片中心的和上极耳电芯边缘距离", "mm", 40.0, 30.0, false, data->service->_paramSyncManager); + auto 铜极连接片中心的和下极耳电芯边缘距离 = genValueControlStruct("焊前检测", "铜极连接片中心的和下极耳电芯边缘距离", "mm", 40.0, 30.0, false, data->service->_paramSyncManager); + auto 铜极连接片角度 = genValueControlStruct("焊前检测", "铜极连接角度", "°", 10.0, -10.0, false, data->service->_paramSyncManager); + double 上下偏移补偿值1 = data->service->_paramSyncManager->get("补偿", "上下偏移补偿值1", 0.0).toDouble(); + double 上下偏移补偿值2 = data->service->_paramSyncManager->get("补偿", "上下偏移补偿值2", 0.0).toDouble(); + cv::Mat sourceImg = data->getOriImg(0); + //sourceImg = cv::imread("D:\\testImages\\T.bmp", -1); + sourceImg( + cv::Rect( + data->service->_paramSyncManager->get("算法裁切", QString::fromStdString(data->cameraKeyName) + "裁切X", 2400).toInt() , + data->service->_paramSyncManager->get("算法裁切", QString::fromStdString(data->cameraKeyName) + "裁切Y", 0).toInt(), + data->service->_paramSyncManager->get("算法裁切", QString::fromStdString(data->cameraKeyName) + "裁切宽", 3000).toInt(), + data->service->_paramSyncManager->get("算法裁切", QString::fromStdString(data->cameraKeyName) + "裁切高", 3648).toInt())).copyTo(sourceImg); + data->insertSegOriginImg(sourceImg); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + HanQianputSnAndDetTime(Paint, data->SNcode, 80); + + HalconCpp::HObject ho_Image_T = MatToHObject(sourceImg); + HalconCpp::HObject ho_Image_L; + HalconCpp::HTuple in_OffsetValue; + in_OffsetValue.Append(上下偏移补偿值1); + in_OffsetValue.Append(上下偏移补偿值2); + //等待副线程传入图像 + bool getImgResult = _detParams->waitImage(ho_Image_L); + + hanQianHalconInput hInput; + hInput.in_Image1 = ho_Image_T; + hInput.in_Image2 = ho_Image_L; + hInput.in_Resolution = pixAccTong; + hInput.in_FilePath = "./halconFiles/ZH_HanYin/"; + hInput.in_LineType = "602"; + hInput.in_OffsetValue = in_OffsetValue; + + auto hOutput = _detParams->evaluater->evaluate(hInput); + HalconCpp::HTuple out_Distance, out_LinePoint, out_Circle, out_Flag, out_Angle, out_NGTails; + if (hOutput.runFlag == true) { + out_Distance = hOutput.result.out_Distance; + out_LinePoint = hOutput.result.out_LinePoint; + out_Circle = hOutput.result.out_Circle; + out_Angle = hOutput.result.out_Angle; + out_Flag = hOutput.result.out_Flag; + out_NGTails = hOutput.result.out_NGTails; + } + else { + out_Flag = 0; + out_NGTails = HalconCpp::HTuple(hOutput.faildResult.c_str()); + } + + int flag = out_Flag.D(); + cvPainter textPainter(90); + if (flag == 1) + { + double Out_Vale_T_1 = out_Distance[0].D() + data->service->_paramSyncManager->get("补偿", "铜芯包间距", -2.7).toDouble();//铜极的芯包间距(mm) + double Out_Vale_T_2 = out_Distance[1].D();//铜极连接片中心的和上极耳电芯边缘距离(mm) + double Out_Vale_T_3 = out_Distance[2].D();//铜极连接片中心的和下极耳电芯边缘距离(mm) + double Out_Vale_T_4 = out_Distance[3].D();//铜极连接片中心的和上极耳水平边缘距离(mm) + double Out_Vale_T_5 = out_Distance[4].D();//铜极连接片中心的和下极耳水平边缘距离(mm) + double Out_Vale_T_6 = out_Distance[5].D();//铜极连接片上边缘的和上极耳下边缘距离(mm) + double Out_Vale_T_7 = out_Distance[6].D();//铜极连接片下边缘的和下极耳上边缘距离(mm) + + double Out_Vale_L_1 = out_Distance[7].D() + data->service->_paramSyncManager->get("补偿", "铝芯包间距", -1.5).toDouble();//铝极的芯包间距(mm) + double Out_Vale_L_2 = out_Distance[8].D();//铝极连接片中心的和上极耳电芯边缘距离(mm) + double Out_Vale_L_3 = out_Distance[9].D();//铝极连接片中心的和下极耳电芯边缘距离(mm) + double Out_Vale_L_4 = out_Distance[10].D();//铝极连接片中心的和上极耳水平边缘距离(mm) + double Out_Vale_L_5 = out_Distance[11].D();//铝极连接片中心的和下极耳水平边缘距离(mm) + double Out_Vale_L_6 = out_Distance[12].D();//铝极连接片上边缘的和上极耳下边缘距离(mm) + double Out_Vale_L_7 = out_Distance[13].D();//铝极连接片下边缘的和下极耳上边缘距离(mm) + + double Out_Angle_T = out_Angle[0].D();//铜极连接片角度偏移 + double Out_Angle_L = out_Angle[1].D();//铝极连接片角度偏移 + + double extremum_Value = out_Distance[14].D() + data->service->_paramSyncManager->get("补偿", "中心距离", 0.45).toDouble();//铜极和铝极的中心距离(mm) + + //发送到另一算法线程 + hanQianDingWeiSubresult _calResult; + _calResult.Out_Vale_L_1 = Out_Vale_L_1; + _calResult.Out_Vale_L_2 = Out_Vale_L_2; + _calResult.Out_Vale_L_3 = Out_Vale_L_3; + _calResult.Out_Vale_L_4 = Out_Vale_L_4; + _calResult.Out_Vale_L_5 = Out_Vale_L_5; + _calResult.Out_Vale_L_6 = Out_Vale_L_6; + _calResult.Out_Vale_L_7 = Out_Vale_L_7; + _calResult.Out_Angle_L = Out_Angle_L; + _calResult.extremum_Value = extremum_Value; + if (out_Circle.Length() > 4) { + _calResult.circleCenter = cv::Point(out_Circle[4].D(), out_Circle[3].D()); + _calResult.radius = out_Circle[5].D(); + } + for (int i = out_LinePoint.Length() / 2; i < out_LinePoint.Length(); i++) { + _calResult.lines.push_back(out_LinePoint[i].D()); + } + _detParams->insertResult(_calResult); + bool valueIsNg = false;//如果此项NG则下面不 + { + auto Out_Vale_T_1_tempResult = getPerfectOutputValue(Out_Vale_T_1, xinBaoJianJuLimit.minThreshold, xinBaoJianJuLimit.maxThreshold, 4.0); + auto Out_Vale_T_2_tempResult = getPerfectOutputValue(Out_Vale_T_2, 铜极连接片中心的和上极耳电芯边缘距离.minThreshold, 铜极连接片中心的和上极耳电芯边缘距离.maxThreshold, 4.0); + auto Out_Vale_T_3_tempResult = getPerfectOutputValue(Out_Vale_T_3, 铜极连接片中心的和下极耳电芯边缘距离.minThreshold, 铜极连接片中心的和下极耳电芯边缘距离.maxThreshold, 4.0); + if (Out_Vale_T_1_tempResult.first) { + valueIsNg = true; + Out_Vale_T_1 = Out_Vale_T_1_tempResult.second; + } + if (Out_Vale_T_2_tempResult.first) { + valueIsNg = true; + Out_Vale_T_2 = Out_Vale_T_2_tempResult.second; + } + if (Out_Vale_T_3_tempResult.first) { + valueIsNg = true; + Out_Vale_T_3 = Out_Vale_T_3_tempResult.second; + } + } + { + double offset = 0; + if (valueIsNg)offset = 3; + valueJudgement(Out_Vale_T_1, xinBaoJianJuLimit, offset, data->halconOK, + "铜极的芯包间距:", "mm", "铜极的芯包间距NG", cv::Point(50, 0), defects, textPainter); + } + { + double offset = 0; + if (valueIsNg)offset = 3; + valueJudgement(Out_Vale_T_2, 铜极连接片中心的和上极耳电芯边缘距离, offset, data->halconOK, + "铜极连接片中心的和上极耳电芯边缘距离:", "mm", "铜极连接片中心的和上极耳电芯边缘距离NG", cv::Point(50, 100), defects, textPainter); + } + { + double offset = 0; + if (valueIsNg)offset = 3; + valueJudgement(Out_Vale_T_3, 铜极连接片中心的和下极耳电芯边缘距离, offset, data->halconOK, + "铜极连接片中心的和下极耳电芯边缘距离:", "mm", "铜极连接片中心的和下极耳电芯边缘距离NG", cv::Point(50, 200), defects, textPainter); + } + + textPainter.appendPaintJob("铜极连接片中心的和上极耳水平边缘距离:" + std::to_string(Out_Vale_T_4), cv::Point(50, 300), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铜极连接片中心的和上极耳水平边缘距离:" + std::to_string(Out_Vale_T_5), cv::Point(50, 400), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铜极连接片上边缘的和上极耳下边缘距离:" + std::to_string(Out_Vale_T_6), cv::Point(50, 500), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铜极连接片下边缘的和下极耳上边缘距离:" + std::to_string(Out_Vale_T_7), cv::Point(50, 600), cv::Scalar(0, 255, 0)); + + valueJudgement(extremum_Value, lianJiePianJianJuLimit, 5.0, data->halconOK, + "铜极和铝极的中心距离:", "mm", "铜极和铝极的中心距离NG", cv::Point(50, 700), defects, textPainter); + + valueJudgement(Out_Angle_T, 铜极连接片角度, 0.0, data->halconOK, + "连接片角度:", "°", "连接片角度NG", cv::Point(50, 800), defects, textPainter); + if (data->halconOK == false) + plcResult = "2"; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(Out_Vale_T_1) + "," + QByteArray::number(extremum_Value));//数据1 + for (int i = 0; i < out_LinePoint.Length() / 8; i++) { + textPainter.appendPaintJob(cv::Point(out_LinePoint[i * 4 + 1].D(), out_LinePoint[i * 4].D()), + cv::Point(out_LinePoint[i * 4 + 3].D(), out_LinePoint[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 10); + } + + if (out_Circle.Length() > 2) { + textPainter.appendPaintJob(cv::Point(out_Circle[1].D(), out_Circle[0].D()), 200, cv::Scalar(0, 255, 0), 5, 1); + textPainter.appendPaintJob(cv::Point(out_Circle[1].D(), out_Circle[0].D()), out_Circle[2].D(), cv::Scalar(0, 255, 0), 5, 0); + } +#pragma region 将数据存入数据库 + data->sqlVec.insert({ "铜极的芯包间距",Out_Vale_T_1 }); + data->sqlVec.insert({ "铜极连接片中心的和上极耳电芯边缘距离",Out_Vale_T_2 }); + data->sqlVec.insert({ "铜极连接片中心的和下极耳电芯边缘距离",Out_Vale_T_3 }); + data->sqlVec.insert({ "铜极连接片中心的和上极耳水平边缘距离",Out_Vale_T_4 }); + data->sqlVec.insert({ "铜极连接片中心的和下极耳水平边缘距离",Out_Vale_T_5 }); + data->sqlVec.insert({ "铜极连接片上边缘的和上极耳下边缘距离",Out_Vale_T_6 }); + data->sqlVec.insert({ "铜极连接片下边缘的和下极耳上边缘距离",Out_Vale_T_7 }); + data->sqlVec.insert({ "铜连接片角度",Out_Angle_T }); + data->sqlVec.insert({ "铜极的芯包间距",_calResult.Out_Vale_L_1 }); + data->sqlVec.insert({ "铝极连接片中心的和上极耳电芯边缘距离",_calResult.Out_Vale_L_2 }); + data->sqlVec.insert({ "铝极连接片中心的和下极耳电芯边缘距离",_calResult.Out_Vale_L_3 }); + data->sqlVec.insert({ "铝极连接片中心的和上极耳水平边缘距离",_calResult.Out_Vale_L_4 }); + data->sqlVec.insert({ "铝极连接片中心的和下极耳水平边缘距离",_calResult.Out_Vale_L_5 }); + data->sqlVec.insert({ "铝极连接片上边缘的和上极耳下边缘距离",_calResult.Out_Vale_L_6 }); + data->sqlVec.insert({ "铝极连接片下边缘的和下极耳上边缘距离",_calResult.Out_Vale_L_7 }); + data->sqlVec.insert({ "铝连接片角度",_calResult.Out_Angle_L }); + data->sqlVec.insert({ "铜极和铝极的中心距离",_calResult.extremum_Value }); +#pragma endregion + + } + else { + cvPainter warnTextPainter(300); + std::string errTails = out_NGTails[0].S().Text(); + hanQianDingWeiSubresult _calResult; + _calResult.algoFailed = true; + warnTextPainter.appendPaintJob("NG" + errTails, cv::Point(0, 50), cv::Scalar(0, 0, 255)); + warnTextPainter.executePaintJobs(Paint); + _detParams->insertResult(_calResult); + defects.push_back("测量NG"); + data->halconOK = false; + } + if (data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", false).toBool()) + plcResult = "1";//强制OK + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + data->setNg(data->aiDetOK, data->halconOK, defects); + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + else + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + + return true; +} +bool hanQianCeLiang_Sub(std::shared_ptr data, zhongHanHanQianParams* _detParams, sx_581_address address) { + try { + std::vectordefects; + //测试算法用 + QByteArray plcResult = "1"; + auto xinBaoJianJuLimit = genValueControlStruct("焊前检测", "芯包间距", "mm", 72.00, 68.00, true, data->service->_paramSyncManager); + auto lianJiePianJianJuLimit = genValueControlStruct("焊前检测", "连接片间距", "mm", 124.00, 122.00, true, data->service->_paramSyncManager); + auto 铝极连接片中心的和上极耳电芯边缘距离 = genValueControlStruct("焊前检测", "铝极连接片中心的和上极耳电芯边缘距离", "mm", 40.0, 30.0, false, data->service->_paramSyncManager); + auto 铝极连接片中心的和下极耳电芯边缘距离 = genValueControlStruct("焊前检测", "铝极连接片中心的和下极耳电芯边缘距离", "mm", 40.0, 30.0, false, data->service->_paramSyncManager); + auto 铝极连接片角度 = genValueControlStruct("焊前检测", "铝极连接角度", "°", 10.0, -10.0, false, data->service->_paramSyncManager); + + cv::Mat sourceImg = data->getOriImg(0); + sourceImg( + cv::Rect( + data->service->_paramSyncManager->get("算法裁切", QString::fromStdString(data->cameraKeyName) + "裁切X", 0).toInt(), + data->service->_paramSyncManager->get("算法裁切", QString::fromStdString(data->cameraKeyName) + "裁切Y", 0).toInt(), + data->service->_paramSyncManager->get("算法裁切", QString::fromStdString(data->cameraKeyName) + "裁切宽", 3000).toInt(), + data->service->_paramSyncManager->get("算法裁切", QString::fromStdString(data->cameraKeyName) + "裁切高", 3648).toInt())).copyTo(sourceImg); + data->insertSegOriginImg(sourceImg); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + HanQianputSnAndDetTime(Paint, data->SNcode, 80); + HObject image = MatToHObject(sourceImg); + //发送图片 + _detParams->insertImg(image); + + cvPainter textPainter(90); + //等待结果 + hanQianDingWeiSubresult _calResult; + bool waitResultResult = _detParams->waitResult(_calResult); + + if (waitResultResult == false) { + data->halconOK = false; + cvPainter warnTextPainter(300); + warnTextPainter.appendPaintJob("NG:TimeOut", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + warnTextPainter.executePaintJobs(Paint); + plcResult = "2"; + data->insertResultImg(Paint); + return false; + } + + if (_calResult.algoFailed == false) { + bool valueIsNg = false;//如果此项NG则下面不 + { + auto Out_Vale_L_1_tempResult = getPerfectOutputValue(_calResult.Out_Vale_L_1, xinBaoJianJuLimit.minThreshold, xinBaoJianJuLimit.maxThreshold, 3.0); + auto Out_Vale_L_2_tempResult = getPerfectOutputValue(_calResult.Out_Vale_L_2, 铝极连接片中心的和上极耳电芯边缘距离.minThreshold, 铝极连接片中心的和上极耳电芯边缘距离.maxThreshold, 3.0); + auto Out_Vale_L_3_tempResult = getPerfectOutputValue(_calResult.Out_Vale_L_3, 铝极连接片中心的和下极耳电芯边缘距离.minThreshold, 铝极连接片中心的和下极耳电芯边缘距离.maxThreshold, 3.0); + if (Out_Vale_L_1_tempResult.first) { + valueIsNg = true; + _calResult.Out_Vale_L_1 = Out_Vale_L_1_tempResult.second; + } + if (Out_Vale_L_2_tempResult.first) { + valueIsNg = true; + _calResult.Out_Vale_L_2 = Out_Vale_L_2_tempResult.second; + } + if (Out_Vale_L_3_tempResult.first) { + valueIsNg = true; + _calResult.Out_Vale_L_3 = Out_Vale_L_3_tempResult.second; + } + } + { + double offset = 0.0; + if (valueIsNg) offset = 3.0; + valueJudgement(_calResult.Out_Vale_L_1, xinBaoJianJuLimit, offset, data->halconOK, + "铝极的芯包间距:", "mm", "铝极的芯包间距NG", cv::Point(50, 0), defects, textPainter); + } + { + double offset = 0.0; + if (valueIsNg) offset = 3.0; + valueJudgement(_calResult.Out_Vale_L_2, 铝极连接片中心的和上极耳电芯边缘距离, offset, data->halconOK, + "铝极连接片中心的和上极耳电芯边缘距离:", "mm", "铝极连接片中心的和上极耳电芯边缘距离NG", cv::Point(50, 100), defects, textPainter); + } + { + double offset = 0.0; + if (valueIsNg) offset = 3.0; + valueJudgement(_calResult.Out_Vale_L_3, 铝极连接片中心的和下极耳电芯边缘距离, 0.0, data->halconOK, + "铝极连接片中心的和下极耳电芯边缘距离:", "mm", "铝极连接片中心的和下极耳电芯边缘距离NG", cv::Point(50, 200), defects, textPainter); + } + + textPainter.appendPaintJob("铝极连接片中心的和上极耳水平边缘距离:" + std::to_string(_calResult.Out_Vale_L_4), cv::Point(50, 300), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铝极连接片中心的和下极耳水平边缘距离:" + std::to_string(_calResult.Out_Vale_L_5), cv::Point(50, 400), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铝极连接片上边缘的和上极耳下边缘距离:" + std::to_string(_calResult.Out_Vale_L_6), cv::Point(50, 500), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("铝极连接片下边缘的和下极耳上边缘距离:" + std::to_string(_calResult.Out_Vale_L_7), cv::Point(50, 600), cv::Scalar(0, 255, 0)); + + valueJudgement(_calResult.extremum_Value, lianJiePianJianJuLimit, 5.0, data->halconOK, + "铜极和铝极的中心距离:", "mm", "铜极和铝极的中心距离NG", cv::Point(50, 700), defects, textPainter); + + valueJudgement(_calResult.Out_Angle_L, 铝极连接片角度, 0.0, data->halconOK, + "连接片角度:", "°", "连接片角度NG", cv::Point(50, 800), defects, textPainter); + if (data->halconOK == false) + plcResult = "2"; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(_calResult.Out_Vale_L_1) + "," + QByteArray::number(_calResult.extremum_Value));//数据1 + for (int i = 0; i < _calResult.lines.size() / 4; i++) { + textPainter.appendPaintJob(cv::Point(_calResult.lines[i * 4 + 1], _calResult.lines[i * 4]), + cv::Point(_calResult.lines[i * 4 + 3], _calResult.lines[i * 4 + 2]), cv::Scalar(0, 255, 0), 10); + } + textPainter.appendPaintJob(cv::Point(_calResult.circleCenter.x, _calResult.circleCenter.y), 200, cv::Scalar(0, 255, 0), 5, 1); + textPainter.appendPaintJob(cv::Point(_calResult.circleCenter.x, _calResult.circleCenter.y), _calResult.radius, cv::Scalar(0, 255, 0), 5, 0); + } + else { + data->halconOK = false; + cvPainter warnTextPainter(300); + warnTextPainter.appendPaintJob("NG:algoError", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + warnTextPainter.executePaintJobs(Paint); + plcResult = "2"; + } + + if (data->service->_paramSyncManager->get("强制OK", QString::fromStdString(data->cameraKeyName) + "强制OK", false).toBool()) + plcResult = "1";//强制OK + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + data->setNg(data->aiDetOK, data->halconOK, defects); + return true; + } + CATCH_EXCEPTIONS; + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + else + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + return true; +} + + +mission* createMission() { + return new _604_ZhongHan_tiejiao_dingbu(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/604_ZhongHan_tiejiao_dingbu.h b/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/604_ZhongHan_tiejiao_dingbu.h new file mode 100644 index 0000000..7d99d1f --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/604_ZhongHan_tiejiao_dingbu.h @@ -0,0 +1,235 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace HDevEngineCpp; +using namespace HalconCpp; +#pragma endregion +struct tiejiaoHalconInput { + HObject in_Image; + HTuple in_Product; + HTuple in_LineType; + HTuple in_Resolution; + HTuple in_FilePath; +}; + +struct tiejiaoHalconOutput { + HTuple out_Circle; + HTuple out_LinePoint; + HTuple out_Rectangle; + HTuple out_NGTails; + HTuple out_Distance; + HTuple out_Flag; +}; + +class ZhongHanTieJiaoParams { +public: + std::string in_Product_Str = ""; + std::shared_ptr> evaluater; + + ZhongHanTieJiaoParams() { + evaluater = std::make_shared>(); + evaluater->init("_3_Pos_CoverGlue_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const tiejiaoHalconInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Product"] = in.in_Product; + inputHTuple["in_LineType"] = in.in_LineType; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + tiejiaoHalconOutput& out) { + out.out_Circle = outputHTuple["out_Circle"]; + out.out_LinePoint = outputHTuple["out_LinePoint"]; + out.out_Rectangle = outputHTuple["out_Rectangle"]; + out.out_NGTails = outputHTuple["out_NGTails"]; + out.out_Distance = outputHTuple["out_Distance"]; + out.out_Flag = outputHTuple["out_Flag"]; + }); + } +}; + +struct hanQianDingWeiSubresult { + double Out_Vale_L_1 = -1;//铝极的包膜距离(mm) + double Out_Vale_L_2 = -1;//铝极连接片中心的和上极耳电芯边缘距离(mm) + double Out_Vale_L_3 = -1;//铝极连接片中心的和下极耳电芯边缘距离(mm) + double Out_Vale_L_4 = -1;//铝极连接片中心的和上极耳水平边缘距离(mm) + double Out_Vale_L_5 = -1;//铝极连接片中心的和下极耳水平边缘距离(mm) + double Out_Vale_L_6 = -1;//铝极连接片上边缘的和上极耳下边缘距离(mm) + double Out_Vale_L_7 = -1;//铝极连接片下边缘的和下极耳上边缘距离(mm) + double Out_Angle_L = -1;//铝极连接片角度偏移(°) + double extremum_Value = -1; + cv::Point circleCenter; + double radius = 1; + std::vector lines; + + bool algoFailed = false; +}; +struct hanQianHalconInput { + HObject in_Image1; + HObject in_Image2; + HTuple in_Resolution; + HTuple in_FilePath; + HTuple in_LineType; + HTuple in_OffsetValue; +}; + +struct hanQianHalconOutput { + HTuple out_Distance; + HTuple out_LinePoint; + HTuple out_Circle; + HTuple out_Angle; + HTuple out_Flag; + HTuple out_NGTails; +}; +class zhongHanHanQianParams { +public: + QMutex* mutex = new QMutex(); + QQueue resultQueue; + int mainResult = 0, subResult = 0;//0 无结果 1OK 2NG + QQueue mats; + std::shared_ptr> evaluater; + + zhongHanHanQianParams() { + evaluater = std::make_shared>(); + evaluater->init("_2_Cal_ZH_LJP_Distance", + [](std::map& inputHObj, + std::map& inputHTuple, + const hanQianHalconInput& in) { + inputHObj["in_Image1"] = in.in_Image1; + inputHObj["in_Image2"] = in.in_Image2; + inputHTuple["in_Resolution"] = in.in_Resolution; + inputHTuple["in_FilePath"] = in.in_FilePath; + inputHTuple["in_LineType"] = in.in_LineType; + inputHTuple["in_OffsetValue"] = in.in_OffsetValue; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + hanQianHalconOutput& out) { + out.out_Distance = outputHTuple["out_Distance"]; + out.out_LinePoint = outputHTuple["out_LinePoint"]; + out.out_Circle = outputHTuple["out_Circle"]; + out.out_Angle = outputHTuple["out_Angle"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NGTails = outputHTuple["out_NGTails"]; + }); + } + ~zhongHanHanQianParams() { + delete mutex; + } + + int waitMs = 3000; + void insertImg(HObject& input) { + QMutexLocker locker(mutex); + if (mats.size() > 0) { + qWarning() << "焊前定位有遗留的图像,已清除"; + mats.clear(); + } + mats.enqueue(input);//发送检测数据到主线程 + } + bool waitImage(HObject& out) { + int cnt = 0; + while (mats.size() == 0 && cnt < waitMs) { + QThread::msleep(2); + cnt += 2; + } + if (mats.size() != 0) { + QMutexLocker locker(mutex); + out = mats.dequeue(); + return true; + } + return false; + } + void insertResult(hanQianDingWeiSubresult input) { + QMutexLocker locker(mutex); + if (resultQueue.size() > 0) { + qWarning() << "焊前定位有遗留的结果,已清除"; + resultQueue.clear(); + } + resultQueue.enqueue(input);//发送检测数据到主线程 + } + bool waitResult(hanQianDingWeiSubresult& out) { + int cnt = 0; + while (resultQueue.size() == 0 && cnt < waitMs) { + QThread::msleep(2); + cnt += 2; + } + if (resultQueue.size() != 0) { + QMutexLocker locker(mutex); + out = resultQueue.dequeue(); + resultQueue.clear(); + return true; + } + return false; + } + +}; + +double getOutputValue(double realValue, double minValue, double maxValue, double offsetValue); + +void HanQianputSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize, const std::string& extraText); +bool hanQianCeLiang_Main(std::shared_ptr data, zhongHanHanQianParams* _detParams, sx_581_address address); +bool hanQianCeLiang_Sub(std::shared_ptr data, zhongHanHanQianParams* _detParams, sx_581_address address); + +void DrawRotatedRect2Mat(cv::Mat& img, cv::RotatedRect rotateRect, cv::Scalar color, int thickness, int linType); +bool TieJiaoZhengMian90(std::shared_ptr data, ZhongHanTieJiaoParams* _detParams, sx_581_address address); +bool TieJiaoZhengMian45(std::shared_ptr data, ZhongHanTieJiaoParams* _detParams, sx_581_address address); + +class _604_ZhongHan_tiejiao_dingbu : public mission { +public: + _581_SX_Single_triggerTemplate tong_1; + _581_SX_Single_triggerTemplate lv_1; + + _581_SX_Single_triggerTemplate< ZhongHanTieJiaoParams> in_tong_90_1; + _581_SX_Single_triggerTemplate< ZhongHanTieJiaoParams> in_tong_45_2; + _581_SX_Single_triggerTemplate< ZhongHanTieJiaoParams> in_tong_45_3; + + _581_SX_Single_triggerTemplate< ZhongHanTieJiaoParams> out_tong_90_4; + _581_SX_Single_triggerTemplate< ZhongHanTieJiaoParams> out_tong_45_5; + _581_SX_Single_triggerTemplate< ZhongHanTieJiaoParams> out_tong_45_6; + + zhongHanHanQianParams detParams; + ZhongHanTieJiaoParams tongParams; + ZhongHanTieJiaoParams lvParams; + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + if (initOnce == false) { + initOnce = true; + } + + tongParams.in_Product_Str = "Cu"; + lvParams.in_Product_Str = "Al"; + } + bool initCustom() { +#pragma region 注册触发器回调 + tong_1.init("Camera1", "焊前定位-相机1", { { 5060, 1,{5100,5070},"5062","5064",5066} }, 1, &detParams, _serviceManager, bridge, hanQianCeLiang_Main, "焊前定位"); + lv_1.init("Camera2", "焊前定位-相机2", { { 5090, 1,{5100,5070},"5092","5094",5096} }, 1, &detParams, _serviceManager, bridge, hanQianCeLiang_Sub, "焊前定位"); + + in_tong_90_1.init("Camera3", "贴胶检测-内-铜-相机1(90°)", { { 5200, 1,{{5120,5140}},"5202","5204",5206} }, 1, &tongParams, _serviceManager, bridge, TieJiaoZhengMian90, "正面贴胶"); + in_tong_45_2.init("Camera4", "贴胶检测-内-铜-相机2(45°)", { { 5210, 1,{5120,5140},"5212","5214",-1} }, 1, &tongParams, _serviceManager, bridge, TieJiaoZhengMian45, "正面贴胶"); + in_tong_45_3.init("Camera5", "贴胶检测-内-铜-相机3(45°)", { { 5220, 1,{5120,5140},"5222","5224",-1} }, 1, &tongParams, _serviceManager, bridge, TieJiaoZhengMian45, "正面贴胶"); + + out_tong_90_4.init("Camera6", "贴胶检测-外-铝-相机4(90°)", { { 5230, 1,{5120,5140},"5232","5234",5236} }, 1, &lvParams, _serviceManager, bridge, TieJiaoZhengMian90, "正面贴胶"); + out_tong_45_5.init("Camera7", "贴胶检测-外-铝-相机5(45°)", { { 5240, 1,{5120,5140},"5242","5244",-1} }, 1, &lvParams, _serviceManager, bridge, TieJiaoZhengMian45, "正面贴胶"); + out_tong_45_6.init("Camera8", "贴胶检测-外-铝-相机6(45°)", { { 5250, 1,{5120,5140},"5252","5254",-1} }, 1, &lvParams, _serviceManager, bridge, TieJiaoZhengMian45, "正面贴胶"); + +#pragma endregion + return true; + } +}; +#pragma once \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/CMakeLists.txt b/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_hanqian_tiejiao_dingbu/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/zhongHan/604_ZhongHan_hanqian_zhengmiantiejiao.json b/src/mission/604/zhongHan/604_ZhongHan_hanqian_zhengmiantiejiao.json new file mode 100644 index 0000000..370b95a --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_hanqian_zhengmiantiejiao.json @@ -0,0 +1,307 @@ +{ + "ImageView": { + "焊前定位-相机1": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 0, + "yPos": 0 + }, + "焊前定位-相机2": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 0, + "yPos": 1 + }, + "贴胶检测-内-铜-相机1(90°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 1, + "yPos": 0 + }, + "贴胶检测-内-铜-相机2(45°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 2, + "yPos": 0 + }, + "贴胶检测-内-铜-相机3(45°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 3, + "yPos": 0 + }, + "贴胶检测-外-铝-相机4(90°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 1, + "yPos": 1 + }, + "贴胶检测-外-铝-相机5(45°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 2, + "yPos": 1 + }, + "贴胶检测-外-铝-相机6(45°)": { + "viewer": [ + "结果图", + "原图" + ], + "xPos": 3, + "yPos": 1 + } + }, + "backend": { + "CameraSetting": [ + { + "connectionName": "DA5977407#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "焊前定位-相机1#原图" + ], + "key": "Camera1", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA7538564#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "焊前定位-相机2#原图" + ], + "key": "Camera2", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA7538568#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "贴胶检测-内-铜-相机1(90°)#原图" + ], + "key": "Camera3", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8520154#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "贴胶检测-内-铜-相机2(45°)#原图" + ], + "key": "Camera4", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA7292898#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "贴胶检测-内-铜-相机3(45°)#原图" + ], + "key": "Camera5", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8520197#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "贴胶检测-外-铝-相机4(90°)#原图" + ], + "key": "Camera6", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA7292899#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "贴胶检测-外-铝-相机5(45°)#原图" + ], + "key": "Camera7", + "oriSaveFormat": [ + "bmp" + ] + }, + { + "connectionName": "DA8520275#SOFTWARE#2000", + "dllPath": "Hd_CameraModule_HIK_General", + "imageShow": [ + "贴胶检测-外-铝-相机6(45°)#原图" + ], + "key": "Camera8", + "oriSaveFormat": [ + "bmp" + ] + } + ], + "Triggers": { + "heartBeatCmd": [ + "writeHeart_short_D5007_1" + ], + "initStr": "init_192.168.1.50_9600_128_6_0", + "key": "finstcp", + "moduleName": "rpc_finsTcp_boost", + "readAllCmd": [ + "read_byte_D5000_800" + ], + "heartBeatIntervalMS": 400, + "sleepIntervalMS": 1 + }, + "handleAiModel": { + "cls": {}, + "det": {}, + "seg": {} + }, + "imgSaveBasePath": "D://CCD存图", + "missionDllPath": "604_ZhongHan_hanqian_tiejiao_dingbu", + "missionSettings": { + "hide": { + "焊前检测": true, + "相机精度": true, + "通用设置": true, + "顶部贴胶": true, + "补偿": true, + "本地读图": true, + "强制OK": true, + "算法裁切": true + }, + "焊前检测": { + "芯包间距上限(mm)": 72.0, + "芯包间距下限(mm)": 68.0, + "芯包间距判定启用": true, + "芯包顶部到圆心距离上限(mm)": 36.0, + "芯包顶部到圆心距离下限(mm)": 34.0, + "芯包顶部到圆心距离判定启用": true, + "连接片间距上限(mm)": 124.0, + "连接片间距下限(mm)": 122.0, + "连接片间距判定启用": true, + "铜极连接片中心的和上极耳电芯边缘距离上限(mm)": 36.0, + "铜极连接片中心的和上极耳电芯边缘距离下限(mm)": 32.0, + "铜极连接片中心的和上极耳电芯边缘距离判定启用": true, + "铜极连接片中心的和下极耳电芯边缘距离上限(mm)": 36.0, + "铜极连接片中心的和下极耳电芯边缘距离下限(mm)": 32.0, + "铜极连接片中心的和下极耳电芯边缘距离启用": true, + "铝极连接片中心的和上极耳电芯边缘距离上限(mm)": 36.0, + "铝极连接片中心的和上极耳电芯边缘距离下限(mm)": 32.0, + "铝极连接片中心的和上极耳电芯边缘距离启用": true, + "铝极连接片中心的和下极耳电芯边缘距离上限(mm)": 36.0, + "铝极连接片中心的和下极耳电芯边缘距离下限(mm)": 32.0, + "铝极连接片中心的和下极耳电芯边缘距离判定启用": true, + "铜极连接角度上限(°)": 10.0, + "铜极连接角度下限(°)": -10.0, + "铜极连接片角度启用": true, + "铝极连接角度上限(°)": 10.0, + "铝极连接角度下限(°)": -10.0, + "铝极连接片角度启用": true, + "铜极连接片中心的和下极耳电芯边缘距离判定启用": false, + "铜极连接角度判定启用": false, + "铝极连接片中心的和上极耳电芯边缘距离判定启用": false, + "铝极连接角度判定启用": false + }, + "补偿": { + "中心距离": -18.7, + "铜芯包间距": 0.0, + "铝芯包间距": 0.0, + "上下偏移补偿值1": -1.0, + "上下偏移补偿值2": 1.0 + }, + "通用设置": { + "存储Cls图": true, + "存储Seg图": true, + "存储原图": true, + "存储结果图": true, + "额外存储NG图片": false, + "仅在NG时保存原图": false, + "仅在NG时保存Seg图": false, + "仅在NG时保存Cls图": false, + "仅存储NG图的内存占用上限": 70.0, + "仅存储结果图的内存占用上限": 80.0, + "不存储任何图片的内存占用上限": 90.0, + "存图汇总NG结果的等待时间(秒)": 3.0, + "启用等待NG汇总存图": true + }, + "顶部贴胶": { + "上连接片的中心到上极耳胶纸的距离上限(mm)": 20.0, + "上连接片的中心到上极耳胶纸的距离下限(mm)": 8.0, + "上连接片的中心到上极耳胶纸的距离判定启用": false, + "下连接片的中心到下极耳胶纸的距离上限(mm)": 20.0, + "下连接片的中心到下极耳胶纸的距离下限(mm)": 8.0, + "下连接片的中心到下极耳胶纸的距离判定启用": false + }, + "相机精度": { + "焊前检测铜极相机精度": 0.035336, + "焊前检测铝极相机精度": 0.035083, + "顶部贴胶检测铜极相机精度": 0.02292, + "顶部贴胶检测铝极相机精度": 0.02292 + }, + "本地读图": { + "多个路径用#分割,数量不足补充黑图": "", + "Camera1本地读图路径": "D:\\testImages\\T_2.bmp", + "Camera1启用本地读图(单次)": false, + "Camera1启用本地读图(多次)": false, + "Camera2本地读图路径": "D:\\testImages\\L_2.bmp", + "Camera2启用本地读图(单次)": false, + "Camera2启用本地读图(多次)": false, + "Camera3本地读图路径": "D:\\testImages\\TJT_OK.bmp", + "Camera3启用本地读图(单次)": false, + "Camera3启用本地读图(多次)": false, + "Camera4本地读图路径": "", + "Camera4启用本地读图(单次)": false, + "Camera4启用本地读图(多次)": false, + "Camera5本地读图路径": "", + "Camera5启用本地读图(单次)": false, + "Camera5启用本地读图(多次)": false, + "Camera6本地读图路径": "D:\\testImages\\TJL_NG.bmp", + "Camera6启用本地读图(单次)": false, + "Camera6启用本地读图(多次)": false, + "Camera7本地读图路径": "", + "Camera7启用本地读图(单次)": false, + "Camera7启用本地读图(多次)": false, + "Camera8本地读图路径": "", + "Camera8启用本地读图(单次)": false, + "Camera8启用本地读图(多次)": false + }, + "算法裁切": { + "Camera1裁切X": 2400, + "Camera1裁切Y": 0, + "Camera1裁切宽": 3000, + "Camera1裁切高": 3648, + "Camera2裁切X": 0, + "Camera2裁切Y": 0, + "Camera2裁切宽": 3000, + "Camera2裁切高": 3648 + }, + "强制OK": { + "Camera1强制OK": false, + "Camera2强制OK": false, + "Camera6强制OK": false, + "Camera3强制OK": false + } + } + }, + "softwareTile": "焊前检测+正面贴胶检测", + "sublUiSetting": {}, + "runCommandPreset": {} +} \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_jixing/604_ZhongHan_silie_jixing.cpp b/src/mission/604/zhongHan/604_ZhongHan_jixing/604_ZhongHan_silie_jixing.cpp new file mode 100644 index 0000000..6f14b21 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_jixing/604_ZhongHan_silie_jixing.cpp @@ -0,0 +1,321 @@ +#include <604_ZhongHan_silie_jixing.h> +#include +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} + +void aiEvaluate(cv::Mat input, const std::shared_ptr& seg, const std::shared_ptr& cls, cv::Mat& Paint, bool& isNG){ + cv::Mat forSeg; + input.copyTo(forSeg); + //cv::resize(forSeg, forSeg, cv::Size(), 0.25, 0.25); + cv::Mat segRes; + if (seg != nullptr) + { + segRes = ~seg->Evaluate(forSeg); + cv::resize(segRes, segRes, cv::Size(), 4, 4); + std::vector>contours; + cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + int cnt = 0; + cvPainter textPainter(300); + for (auto& contour : contours) { + cv::Rect rect = cv::boundingRect(contour); + if (rect.width < 250 || rect.height < 250) + continue; + cv::Mat clsImg = cutImg2Cls(rect, input); + cnt++; + auto claResult = cls->Evaluate(clsImg); + switch (claResult.classes) + { + case 0://OK + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 3); + break; + case 1://NG + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + textPainter.appendPaintJob("NG", cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255)); + isNG = true; + break; + } + } + textPainter.executePaintJobs(Paint); + } + else { + isNG = false; + } + +} +bool lieHenJianCe90(std::shared_ptr data, LieHenJianCeParams* _detParams, sx_581_address address){ + try { + bool siLieEnable = data->service->_paramSyncManager->get("焊后模型", "撕裂NG启用", true).toBool(); + bool fanZheEnable = data->service->_paramSyncManager->get("焊后模型", "翻折NG启用", true).toBool(); + //data->getOriImg(0) = cv::imread("D:/testimg/" + data->cameraKeyName + ".bmp", -1); + cv::Mat sourceImage = data->getOriImg(0); + //sourceImage = cv::imread("D:/testimg/60A01MB5601FB5A285361_60A01MB5601FBFJ255378_Camera1_ORI_0_07-41-33-604.bmp", -1); + cv::Mat Paint; + sourceImage.copyTo(Paint); + QByteArray plcResult = "1"; + std::vectordefects; + cv::Mat afterDingWei; + {//定位 + cv::Mat forDingWei; + cv::resize(sourceImage, forDingWei, cv::Size(), 0.1, 0.1); + cv::Mat dingWeiResult = ~_detParams->dingwei->Evaluate(forDingWei); + cv::Rect dingWeiRect = findMaxAreaContour(dingWeiResult); + if (dingWeiRect.width < 150 || dingWeiRect.height < 150) { + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG2", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + defects.push_back("定位失败"); + data->insertSegOriginImg(forDingWei); + data->insertSegOriginImg(dingWeiResult); + data->insertResultImg(Paint); + data->aiDetOK = false; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return false; + } + dingWeiRect.x *= 10; dingWeiRect.y *= 10; dingWeiRect.width *= 10; dingWeiRect.height *= 10; + dingWeiRect.y -= 75; dingWeiRect.height += 150; + sourceImage(dingWeiRect & cv::Rect(0, 0, sourceImage.cols, sourceImage.rows)).copyTo(afterDingWei); + cv::resize(afterDingWei, afterDingWei, cv::Size(), 0.5, 0.5); + afterDingWei.copyTo(Paint); + } + //检测 + + data->insertSegOriginImg(afterDingWei); + cvPainter textPainter(300); + std::vector>clsImgsSave; + if (data->cameraKeyName == "Camera1") { + cv::Mat segRes = ~_detParams->seg_diBu->Evaluate(afterDingWei); + std::vector>contours; + cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + int clsIndex = 0; + double probThreshold = data->service->_paramSyncManager->get("底部焊后", QString::fromStdString(data->cameraKeyName) + "prob阈值", 0.8).toDouble(); + double widthThreshold = data->service->_paramSyncManager->get("底部焊后", QString::fromStdString(data->cameraKeyName) + "宽阈值", 400).toDouble(); + double heightThreshold = data->service->_paramSyncManager->get("底部焊后", QString::fromStdString(data->cameraKeyName) + "高阈值", 160).toDouble(); + for (auto& contour : contours) { + clsIndex++; + cv::Rect rect = cv::boundingRect(contour); + if (rect.width < widthThreshold || rect.height < heightThreshold) + continue; + auto clsImgs = cutImg2ClsVec(rect, afterDingWei); + bool isNg = false; + for (auto& clsImg : clsImgs) { + auto claResult = _detParams->cls_diBu->Evaluate(clsImg); + double prob = claResult.prob; + if (claResult.classes == 1 && prob > probThreshold) { + isNg = true; + } + clsImgsSave.push_back({ "result" + std::to_string(claResult.classes) + "_prob" + std::to_string(prob),clsImg }); + } + if (isNg == true) { + defects.push_back("底部焊后NG"); + cv::drawContours(Paint, contours, clsIndex - 1, cv::Scalar(0, 0, 255), 2); + data->aiDetOK = false; + } + else { + cv::drawContours(Paint, contours, clsIndex - 1, cv::Scalar(0, 255, 0), 2); + } + + } + } + data->insertClsOriginImg(clsImgsSave); + //data->clsOriginImgs.insert(data->clsOriginImgs.end(),) + if (data->aiDetOK == false) { + textPainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->halconOK = false; + plcResult = "2"; + } + else { + textPainter.appendPaintJob("OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + if (!data->service->_paramSyncManager->get("底部焊后", "底部焊后判定启用", true).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + + cvPainter textPainterx(40); + textPainterx.appendPaintJob("SN:" + data->SNcode, cv::Point(0, Paint.rows - 80), cv::Scalar(0, 255, 0)); + textPainterx.appendPaintJob(QDateTime::currentDateTime().toString().toStdString(), cv::Point(0, Paint.rows - 40), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + textPainterx.executePaintJobs(Paint); + data->insertResultImg(Paint); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; + + +} + +bool lieHenJianCe45(std::shared_ptr data, LieHenJianCeParams* _detParams, sx_581_address address) { + try + { + bool siLieEnable = data->service->_paramSyncManager->get("焊后45°模型", "撕裂NG启用", true).toBool(); + bool fanZheEnable = data->service->_paramSyncManager->get("焊后45°模型", "翻折NG启用", true).toBool(); + cv::Mat sourceImg; + cv::Rect cutROI; + cutROI = cv::Rect(1600, 900, 2200, 1400) & cv::Rect(0, 0, data->getOriImg(0).cols, data->getOriImg(0).rows); + data->getOriImg(0)(cutROI).copyTo(sourceImg); + cv::Mat Paint; + sourceImg.copyTo(Paint); + putSnAndDetTime(Paint, data->SNcode, 60); + cvPainter textPainter(300); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + bool isNG = false; + QByteArray plcResult = "1"; + textPainter.appendPaintJob("OK", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + //cv::rectangle(Paint, cutROI, cv::Scalar(0, 255, 0), 5); + data->insertResultImg(Paint); + + sourceImg.release(); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + return true; + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; + +} + +bool jiXingJianCe(std::shared_ptr data, JiXingJianCeParams* _detParams, sx_581_address address){ + try + { + /*if (indexTest < 17) + img2D = cv::imread("./testImages/" + std::to_string(indexTest++) + ".bmp", -1);*/ + + //存图数据加保存队列,加锁,避免数据访问冲突 + //cv::Rect cutRect(0, 0, img2D.cols, img2D.rows);//裁剪出有效区域 + //cutRect = cutRect & cv::Rect(0, 0, img2D.cols, img2D.rows); + cv::Mat sourceImg; + data->getOriImg(0).copyTo(sourceImg); + std::vectordefects; + + cv::Mat Paint; + sourceImg.copyTo(Paint); + putSnAndDetTime(Paint, data->SNcode, 60); + //极性检测 + jiXingHalconInput hInput; + hInput.in_Image = MatToHObject(sourceImg); + hInput.in_FilePath = "./halconFiles/"; + hInput.in_LineType = "604"; + auto hOutput = _detParams->evaluater->evaluate(hInput); + HTuple out_ColorType; + HTuple out_Flag; + HTuple out_InspectionPosition; + HTuple out_NgTails; + if (hOutput.runFlag == true) { + out_ColorType = hOutput.result.out_ColorType; + out_Flag = hOutput.result.out_Flag; + out_InspectionPosition = hOutput.result.out_InspectionPosition; + out_NgTails = hOutput.result.out_NgTails; + } + else { + out_Flag = 0; + out_NgTails = HTuple(hOutput.faildResult.c_str()); + } + //相机2 OK为铜 NG为铝 + QByteArray plcResult = "2"; + cvPainter textPainter(300); + if (out_Flag.D() == 1) + { + if (out_InspectionPosition.Length() > 3) { + for (int i = 0; i < out_InspectionPosition.Length() / 4; i++) { + cv::Rect rect = cv::Rect(out_InspectionPosition[i * 4 + 0], \ + out_InspectionPosition[i * 4 + 1], \ + out_InspectionPosition[i * 4 + 2], \ + out_InspectionPosition[i * 4 + 3]); + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 255), 2); + } + } + + std::string jx_Type = out_ColorType.S().Text(); + if (jx_Type == "LV") { + //铝 + defects.push_back("极性NG"); + cv::putText(Paint, "NG", cv::Point(0, 500), 2, 10, cv::Scalar(0, 0, 255), 5); + data->halconOK = false; + } + else if (jx_Type == "CU") { + //铜 + textPainter.appendPaintJob("铜极", cv::Point(0, 0), cv::Scalar(0, 255, 0)); + cv::putText(Paint, "OK", cv::Point(0, 500), 2, 10, cv::Scalar(0, 255, 0), 5); + plcResult = "1"; + } + else { + textPainter.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + defects.push_back("未知NG"); + data->halconOK = false; + } + } + else { + std::string temp_NGTails = out_NgTails.S().Text(); + textPainter.appendPaintJob("NG", cv::Point(0, 50), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob(temp_NGTails.c_str(), cv::Point(0, 350), cv::Scalar(0, 0, 255)); + defects.push_back("极性算法NG"); + data->halconOK = false; + } + if (!data->service->_paramSyncManager->get("极性检测", "极性判定启用", true).toBool()) + plcResult = "1"; + sourceImg.release(); + textPainter.executePaintJobs(Paint); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 + return true; + + +} + + +mission* createMission() { + return new _604_ZhongHan_silie_jixing(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_jixing/604_ZhongHan_silie_jixing.h b/src/mission/604/zhongHan/604_ZhongHan_jixing/604_ZhongHan_silie_jixing.h new file mode 100644 index 0000000..39c329c --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_jixing/604_ZhongHan_silie_jixing.h @@ -0,0 +1,121 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +struct jiXingHalconInput { + HObject in_Image; + HTuple in_FilePath; + HTuple in_LineType; +}; + +struct jiXingHalconOutput { + HTuple out_ColorType; + HTuple out_Flag; + HTuple out_InspectionPosition; + HTuple out_NgTails; +}; + +class LieHenJianCeParams { +public: + std::shared_ptr dingwei = nullptr; + std::shared_ptr seg_diBu = nullptr; + std::shared_ptr cls_diBu = nullptr; + bool isTong = false; +}; + +class JiXingJianCeParams { +public: + std::shared_ptr> evaluater; + JiXingJianCeParams() { + evaluater = std::make_shared>(); + evaluater->init("_2_JX_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const jiXingHalconInput& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_FilePath"] = in.in_FilePath; + inputHTuple["in_LineType"] = in.in_LineType; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + jiXingHalconOutput& out) { + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgTails = outputHTuple["out_NgTails"]; + out.out_ColorType = outputHTuple["out_ColorType"]; + out.out_InspectionPosition = outputHTuple["out_InspectionPosition"]; + }); + } +}; + +void aiEvaluate(cv::Mat input, const std::shared_ptr& seg, const std::shared_ptr& cls, cv::Mat& Paint, bool& isNG); +bool lieHenJianCe90(std::shared_ptr data, LieHenJianCeParams* _detParams, sx_581_address address); + +bool lieHenJianCe45(std::shared_ptr data, LieHenJianCeParams* _detParams, sx_581_address address); + +bool jiXingJianCe(std::shared_ptr data, JiXingJianCeParams* _detParams, sx_581_address address); + + + + +class _604_ZhongHan_silie_jixing : public mission { +public: + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_tong_1; + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_tong_2; + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_tong_3; + + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_lv_4; + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_lv_5; + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_lv_6; + + _581_SX_Single_triggerTemplate< JiXingJianCeParams> jixing_cu; + + JiXingJianCeParams tongJiXingParams; + LieHenJianCeParams tong90Params; + LieHenJianCeParams lv90Params; + + LieHenJianCeParams tong45Params; + LieHenJianCeParams lv45Params; + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_604.hdpl", bridge); + if (initOnce == false) { + tong90Params.dingwei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/dingwei90_seg.BHD", 16, 0, "中焊极性90铜定位Seg网"); + tong90Params.seg_diBu = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/seg_hanHouDiBu_T.BHD", 16, 0, "中焊极性90铜底部Seg网"); + tong90Params.cls_diBu = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/cls_hanHouDiBu_T.BHD", 1, 0, "中焊极性90铜底部Cls网"); + lv90Params.dingwei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/dingwei90_seg.BHD", 16, 1, "中焊极性90铝定位Seg网"); + initOnce = true; + } + + + + lv90Params.isTong = false; + } + bool initCustom() { +#pragma region 注册触发器回调 + /* liehen_tong_1.init("Camera1", "裂痕检测-铜-相机1", { { 5280, 1,{5160,5180},"5282","5284",-1} }, 1, &tong90Params, _serviceManager, bridge, lieHenJianCe90, "CCD裂痕检测"); + liehen_tong_2.init("Camera2", "裂痕检测-铜-相机2", { { 5290, 1,{5160,5180},"5292","5294",-1} }, 1, &tong45Params, _serviceManager, bridge, lieHenJianCe45, "CCD裂痕检测"); + liehen_tong_3.init("Camera3", "裂痕检测-铜-相机3", { { 5300, 1,{5160,5180},"5302","5304",-1} }, 1, &tong45Params, _serviceManager, bridge, lieHenJianCe45, "CCD裂痕检测"); + + liehen_lv_4.init("Camera4", "裂痕检测-铝-相机4", { { 5310, 1,{5160,5180},"5312","5314",-1} }, 1, &lv90Params, _serviceManager, bridge, lieHenJianCe90, "CCD裂痕检测"); + liehen_lv_5.init("Camera5", "裂痕检测-铝-相机5", { { 5320, 1,{5160,5180},"5322","5324",-1} }, 1, &lv45Params, _serviceManager, bridge, lieHenJianCe45, "CCD裂痕检测"); + liehen_lv_6.init("Camera6", "裂痕检测-铝-相机6", { { 5330, 1,{5160,5180},"5332","5334",-1} }, 1, &lv45Params, _serviceManager, bridge, lieHenJianCe45, "CCD裂痕检测");*/ + + jixing_cu.init("Camera1", "极性检测-相机1", { { 5000, 1,{5010},"5002","5004",-1} }, 1, &tongJiXingParams, _serviceManager, bridge, jiXingJianCe, "CCD极性检测"); + +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_jixing/CMakeLists.txt b/src/mission/604/zhongHan/604_ZhongHan_jixing/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_jixing/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/604_ZhongHan_silie_jixing.cpp b/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/604_ZhongHan_silie_jixing.cpp new file mode 100644 index 0000000..7df31d1 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/604_ZhongHan_silie_jixing.cpp @@ -0,0 +1,217 @@ +#include <604_ZhongHan_silie_jixing.h> +#include +void putSnAndDetTime(cv::Mat& Paint, const std::string& sn, int textSize = 30, const std::string& extraText = "") { + cvPainter textPainter(textSize); + textPainter.appendPaintJob("SN:" + sn + QDateTime::currentDateTime().toString().toStdString() + extraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); +} + +void aiEvaluate(cv::Mat input, std::shared_ptr seg, std::shared_ptr cls, cv::Mat& Paint, bool& isNG){ + cv::Mat forSeg; + input.copyTo(forSeg); + //cv::resize(forSeg, forSeg, cv::Size(), 0.25, 0.25); + cv::Mat segRes; + if (seg != nullptr) + { + segRes = ~seg->Evaluate(forSeg); + cv::resize(segRes, segRes, cv::Size(), 4, 4); + std::vector>contours; + cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + int cnt = 0; + for (auto& contour : contours) { + cv::Rect rect = cv::boundingRect(contour); + if (rect.width < 250 || rect.height < 250) + continue; + cv::Mat clsImg = cutImg2Cls(rect, input); + cnt++; + auto clsResult = cls->Evaluate(clsImg); + cvPainter textPainter(300); + switch (clsResult.classes) + { + case 0://OK + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 3); + break; + case 1://NG + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + textPainter.appendPaintJob("NG",cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255)); + textPainter.executePaintJobs(Paint); + isNG = true; + break; + } + } + } + else { + isNG = false; + } + +} +bool lieHenJianCe90(std::shared_ptr data, LieHenJianCeParams* _detParams, sx_581_address address){ + try { + bool siLieEnable = data->service->_paramSyncManager->get("焊后模型", "撕裂NG启用", true).toBool(); + bool fanZheEnable = data->service->_paramSyncManager->get("焊后模型", "翻折NG启用", true).toBool(); + //data->getOriImg(0) = cv::imread("D:/testimg/" + data->cameraKeyName + ".bmp", -1); + cv::Mat sourceImage = data->getOriImg(0); + //sourceImage = cv::imread("D:/testimg/60A01MB5601FB5A285361_60A01MB5601FBFJ255378_Camera1_ORI_0_07-41-33-604.bmp", -1); + cv::Mat Paint; + sourceImage.copyTo(Paint); + QByteArray plcResult = "1"; + std::vectordefects; + cv::Mat afterDingWei; + {//定位 + cv::Mat forDingWei; + cv::resize(sourceImage, forDingWei, cv::Size(), 0.1, 0.1); + cv::Mat dingWeiResult = ~_detParams->dingwei->Evaluate(forDingWei); + cv::Rect dingWeiRect = findMaxAreaContour(dingWeiResult); + if (dingWeiRect.width < 150 || dingWeiRect.height < 150) { + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG2", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + defects.push_back("定位失败"); + data->insertSegOriginImg(forDingWei); + data->insertSegOriginImg(dingWeiResult); + data->insertResultImg(Paint); + data->aiDetOK = false; + plcResult = "2"; + if (!data->service->_paramSyncManager->get("底部焊后", "底部焊后判定启用", true).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + dingWeiRect.x *= 10; dingWeiRect.y *= 10; dingWeiRect.width *= 10; dingWeiRect.height *= 10; + dingWeiRect.y -= 75; dingWeiRect.height += 150; + sourceImage(dingWeiRect & cv::Rect(0, 0, sourceImage.cols, sourceImage.rows)).copyTo(afterDingWei); + cv::resize(afterDingWei, afterDingWei, cv::Size(), 0.5, 0.5); + afterDingWei.copyTo(Paint); + } + //检测 + + data->insertSegOriginImg(afterDingWei); + cvPainter textPainter(300); + std::vector>clsImgsSave; + if (data->cameraKeyName == "Camera1") { + cv::Mat segRes = ~_detParams->seg_diBu->Evaluate(afterDingWei); + std::vector>contours; + cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + int clsIndex = 0; + double probThreshold = data->service->_paramSyncManager->get("底部焊后", QString::fromStdString(data->cameraKeyName) + "prob阈值", 0.8).toDouble(); + double widthThreshold = data->service->_paramSyncManager->get("底部焊后", QString::fromStdString(data->cameraKeyName) + "宽阈值", 400).toDouble(); + double heightThreshold = data->service->_paramSyncManager->get("底部焊后", QString::fromStdString(data->cameraKeyName) + "高阈值", 160).toDouble(); + for (auto& contour : contours) { + clsIndex++; + cv::Rect rect = cv::boundingRect(contour); + if (rect.width < widthThreshold || rect.height < heightThreshold) + continue; + auto clsImgs = cutImg2ClsVec(rect, afterDingWei); + bool isNg = false; + for (auto& clsImg : clsImgs) { + auto clsResult = _detParams->cls_diBu->Evaluate(clsImg); + if (clsResult.classes == 1 && clsResult.prob > probThreshold) { + isNg = true; + } + clsImgsSave.push_back({ "result" + std::to_string(clsResult.classes) + "_prob" + std::to_string(clsResult.prob),clsImg }); + } + if (isNg == true) { + defects.push_back("底部焊后NG"); + cv::drawContours(Paint, contours, clsIndex - 1, cv::Scalar(0, 0, 255), 2); + data->aiDetOK = false; + } + else { + cv::drawContours(Paint, contours, clsIndex - 1, cv::Scalar(0, 255, 0), 2); + } + + } + } + data->insertClsOriginImg(clsImgsSave); + //data->clsOriginImgs.insert(data->clsOriginImgs.end(),) + if (data->aiDetOK == false) { + textPainter.appendPaintJob("NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->halconOK = false; + plcResult = "2"; + } + else { + textPainter.appendPaintJob("OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + } + if (!data->service->_paramSyncManager->get("底部焊后", "底部焊后判定启用", true).toBool()) + plcResult = "1"; + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + + textPainter.executePaintJobs(Paint); + cvPainter textPainterx(40); + textPainterx.appendPaintJob("SN:" + data->SNcode, cv::Point(0, Paint.rows - 80), cv::Scalar(0, 255, 0)); + textPainterx.appendPaintJob(QDateTime::currentDateTime().toString().toStdString(), cv::Point(0, Paint.rows - 40), cv::Scalar(0, 255, 0)); + textPainterx.executePaintJobs(Paint); + data->insertResultImg(Paint); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + return true; + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; + + +} + +bool lieHenJianCe45(std::shared_ptr data, LieHenJianCeParams* _detParams, sx_581_address address) { + try + { + bool siLieEnable = data->service->_paramSyncManager->get("焊后45°模型", "撕裂NG启用", true).toBool(); + bool fanZheEnable = data->service->_paramSyncManager->get("焊后45°模型", "翻折NG启用", true).toBool(); + cv::Mat sourceImg; + cv::Rect cutROI; + cutROI = cv::Rect(1600, 900, 2200, 1400) & cv::Rect(0, 0, data->getOriImg(0).cols, data->getOriImg(0).rows); + data->getOriImg(0)(cutROI).copyTo(sourceImg); + cv::Mat Paint; + sourceImg.copyTo(Paint); + putSnAndDetTime(Paint, data->SNcode, 60); + cvPainter textPainter(300); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + bool isNG = false; + QByteArray plcResult = "1"; + textPainter.appendPaintJob("OK", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + //cv::rectangle(Paint, cutROI, cv::Scalar(0, 255, 0), 5); + data->insertResultImg(Paint); + + sourceImg.release(); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + return true; + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cvPainter textPainter(15); + textPainter.appendPaintJob("NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + textPainter.executePaintJobs(Paint); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + return true; + +} +mission* createMission() { + return new _604_ZhongHan_silie_jixing(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/604_ZhongHan_silie_jixing.h b/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/604_ZhongHan_silie_jixing.h new file mode 100644 index 0000000..86de276 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/604_ZhongHan_silie_jixing.h @@ -0,0 +1,89 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +class LieHenJianCeParams { +public: + std::shared_ptr dingwei = nullptr; + std::shared_ptr seg_diBu = nullptr; + std::shared_ptr cls_diBu = nullptr; + bool isTong = false; +}; + +class JiXingJianCeParams { +public: + +}; + +void aiEvaluate(cv::Mat input, handdleAI::pSegmentation* seg, handdleAI::pClassification* cls, cv::Mat& Paint, bool& isNG); +bool lieHenJianCe90(std::shared_ptr data, LieHenJianCeParams* _detParams, sx_581_address address); + +bool lieHenJianCe45(std::shared_ptr data, LieHenJianCeParams* _detParams, sx_581_address address); + +bool jiXingJianCe(std::shared_ptr data, JiXingJianCeParams* _detParams, sx_581_address address); + + + + +class _604_ZhongHan_silie_jixing : public mission { +public: + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_tong_1; + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_tong_2; + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_tong_3; + + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_lv_4; + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_lv_5; + _581_SX_Single_triggerTemplate< LieHenJianCeParams> liehen_lv_6; + + _581_SX_Single_triggerTemplate< JiXingJianCeParams> jixing_cu; + + JiXingJianCeParams tongJiXingParams; + LieHenJianCeParams tong90Params; + LieHenJianCeParams lv90Params; + + LieHenJianCeParams tong45Params; + LieHenJianCeParams lv45Params; + bool initOnce = false; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_602.hdpl", bridge); + + if (initOnce == false) { + tong90Params.dingwei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/dingwei90_seg.BHD", 16, 0, "中焊90度定位网"); + tong90Params.seg_diBu = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/seg_hanHouDiBu_T.BHD", 16, 0, "中焊铜底部Seg网"); + tong90Params.cls_diBu = _serviceManager->_handleAiManager->loadClsModel("./handleaiModels/cls_hanHouDiBu_T.BHD", 1, 0, "中焊铜底部Cls网"); + lv90Params.dingwei = _serviceManager->_handleAiManager->loadSegModel("./handleaiModels/dingwei90_seg.BHD", 16, 1, "中焊90度定位网"); + initOnce = true; + } + + + + lv90Params.isTong = false; + } + bool initCustom() { +#pragma region 注册触发器回调 + liehen_tong_1.init("Camera1", "裂痕检测-铜-相机1", { { 5280, 1,{5160,5180},"5282","5284",-1} }, 1, &tong90Params, _serviceManager, bridge, lieHenJianCe90, "CCD裂痕检测"); + liehen_tong_2.init("Camera2", "裂痕检测-铜-相机2", { { 5290, 1,{5160,5180},"5292","5294",-1} }, 1, &tong45Params, _serviceManager, bridge, lieHenJianCe45, "CCD裂痕检测"); + liehen_tong_3.init("Camera3", "裂痕检测-铜-相机3", { { 5300, 1,{5160,5180},"5302","5304",-1} }, 1, &tong45Params, _serviceManager, bridge, lieHenJianCe45, "CCD裂痕检测"); + + liehen_lv_4.init("Camera4", "裂痕检测-铝-相机4", { { 5310, 1,{5160,5180},"5312","5314",-1} }, 1, &lv90Params, _serviceManager, bridge, lieHenJianCe90, "CCD裂痕检测"); + liehen_lv_5.init("Camera5", "裂痕检测-铝-相机5", { { 5320, 1,{5160,5180},"5322","5324",-1} }, 1, &lv45Params, _serviceManager, bridge, lieHenJianCe45, "CCD裂痕检测"); + liehen_lv_6.init("Camera6", "裂痕检测-铝-相机6", { { 5330, 1,{5160,5180},"5332","5334",-1} }, 1, &lv45Params, _serviceManager, bridge, lieHenJianCe45, "CCD裂痕检测"); + + //jixing_cu.init("Camera7", "极性检测-相机1", { { 5000, 1,{5010},"5002","5004",-1} }, 1, &tongJiXingParams, _serviceManager, bridge, jiXingJianCe, "CCD极性检测"); + +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/CMakeLists.txt b/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/604/zhongHan/604_ZhongHan_silie_jixing/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/604/zhongHan/CMakeLists.txt b/src/mission/604/zhongHan/CMakeLists.txt new file mode 100644 index 0000000..f9a95fd --- /dev/null +++ b/src/mission/604/zhongHan/CMakeLists.txt @@ -0,0 +1,11 @@ +# CMakeList.txt: 遍历文件夹 添加子项目 + +set(OUR_LIBS "") +SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF) +message("OUR_LIBS :" ${OUR_LIBS}) + + +# 遍历子库 +foreach(dll ${OUR_LIBS}) + add_subdirectory(${dll}) +endforeach(dll ${}) diff --git a/src/mission/581-BaoMo-Cam1213/581-BaoMo-Cam1213.cpp b/src/mission/old/581-BaoMo-Cam1213/581-BaoMo-Cam1213.cpp similarity index 79% rename from src/mission/581-BaoMo-Cam1213/581-BaoMo-Cam1213.cpp rename to src/mission/old/581-BaoMo-Cam1213/581-BaoMo-Cam1213.cpp index bfffca1..be9ec5b 100644 --- a/src/mission/581-BaoMo-Cam1213/581-BaoMo-Cam1213.cpp +++ b/src/mission/old/581-BaoMo-Cam1213/581-BaoMo-Cam1213.cpp @@ -1,5 +1,30 @@ #include <581-BaoMo-Cam1213.h> +#include +std::pair getOutputValue( + double realValue, + double minValue, + double maxValue, + double offsetValue) +{ + static thread_local std::mt19937 rng(std::random_device{}()); + std::uniform_real_distribution dist(0.0, 0.1); //随机数 + + if (realValue < minValue) {// 低于下限 + if (minValue - realValue <= offsetValue) {// 在补偿范围内 + return { true, minValue + dist(rng) };// 返回补偿后的值 + } + return { false, realValue }; // 不在在补偿范围内 返回真实值 + } + else if (realValue > maxValue) { + if (realValue - maxValue <= offsetValue) {// 在补偿范围内 + return { true, maxValue - dist(rng) };// 返回补偿后的值 + } + return { false, realValue }; // 不在在补偿范围内 返回真实值 + } + + return { false, realValue };// 在范围内,无需补偿,返回真实值 +} struct halconResult { bool isNg = false; double resultX = -99; @@ -28,7 +53,7 @@ void exceptionResume(std::shared_ptr data, yh_581_address address, QStr data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1,0,1");//发送结果 } -halconResult dituo(std::shared_ptr data, cv::Mat& src, cv::Mat& Paint, bool isLeft, std::vector < std::string>& defects) { +halconResult dituo(std::shared_ptr data, baoMoHouDiBuDetParams* _detParams,cv::Mat& src, cv::Mat& Paint, bool isLeft, std::vector < std::string>& defects) { halconResult result; try { double widthThreshold = data->service->_paramSyncManager->get("底托检测", "宽度阈值(mm)", 2.0).toDouble(); @@ -54,7 +79,7 @@ halconResult dituo(std::shared_ptr data, cv::Mat& src, cv::Mat& Paint, myEngine.StartDebugServer(); procedureCall.SetWaitForDebugConnection(true);*/ float resolution = data->service->_paramSyncManager->get("相机精度", QString::fromStdString(data->cameraKeyName) + "精度", 0.040888).toFloat(); - HTuple in_Par, in_FilePath("./halconFiles/"); + /* HTuple in_Par, in_FilePath("./halconFiles/"); in_Par[0] = widthThreshold; in_Par[1] = heightThreshold; in_Par[2] = areaThreshold; @@ -67,21 +92,29 @@ halconResult dituo(std::shared_ptr data, cv::Mat& src, cv::Mat& Paint, HalconCpp::HTupleVector out_VectorX = procedureCall.GetOutputCtrlParamVector("out_VectorX"); HalconCpp::HTupleVector out_VectorY = procedureCall.GetOutputCtrlParamVector("out_VectorY"); HalconCpp::HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); - HalconCpp::HTuple out_NgDetails = procedureCall.GetOutputCtrlParamTuple("out_NgDetails"); + HalconCpp::HTuple out_NgDetails = procedureCall.GetOutputCtrlParamTuple("out_NgDetails");*/ + + baoMoHouDiBuDetParams::input input; + input.in_Image = MatToHObject(src); + input.in_Par[0] = widthThreshold; + input.in_Par[1] = heightThreshold; + input.in_Par[2] = areaThreshold; + input.in_Resolution = resolution; + auto output = _detParams->evaluater.evaluate(input); //HTuple out_RecX = procedureCall.GetOutputCtrlParamTuple("out_RecX"); //HTuple out_RecY = procedureCall.GetOutputCtrlParamTuple("out_RecY"); - int outFlag = out_Flag.D(); + int outFlag = output.result.out_Flag.D(); std::vector> clsImgs; TextRendererZH textPainterdefect(Paint.cols, Paint.rows, 30); if (outFlag == 0) { cv::Mat whiteMat = cv::Mat::zeros(cv::Size(src.cols, src.rows), CV_8UC1); std::vector> contours; - int l1 = out_VectorX.Length(), l2 = out_VectorY.Length(), l3 = out_NgDetails.Length(); - for (int m = 0; m < out_VectorX.Length() && m < out_VectorY.Length() && m < out_NgDetails.Length(); m++) { - HalconCpp::HTuple out_X = out_VectorX[m].ConvertVectorToTuple(); - HalconCpp::HTuple out_Y = out_VectorY[m].ConvertVectorToTuple(); + int l1 = output.result.out_VectorX.size(), l2 = output.result.out_VectorY.size(), l3 = output.result.out_NgDetails.Length(); + for (int m = 0; m < output.result.out_VectorX.size() && m < output.result.out_VectorY.size() && m < output.result.out_NgDetails.Length(); m++) { + HalconCpp::HTuple out_X = output.result.out_VectorX[m]; + HalconCpp::HTuple out_Y = output.result.out_VectorY[m]; - std::string ngDetails = (out_NgDetails[m].ToTuple().ToString().Text()); + std::string ngDetails = (output.result.out_NgDetails[m].ToTuple().ToString().Text()); //输出的点集不是正常的轮廓点集,把点集在黑图上画出来,在用opencv重新找一遍轮廓 @@ -94,7 +127,7 @@ halconResult dituo(std::shared_ptr data, cv::Mat& src, cv::Mat& Paint, } //画轮廓 std::vector> contoursPaint; - cv::findContours(whiteMat, contoursPaint, cv::RETR_TREE, cv::CHAIN_APPROX_NONE); + cv::findContours(whiteMat, contoursPaint, cv::RETR_TREE, cv::CHAIN_APPROX_NONE); int maxIndex = 0, maxArea = 0; for (int i = 0; i < contoursPaint.size(); i++) { cv::Rect rect = cv::boundingRect(contoursPaint[i]); @@ -103,7 +136,7 @@ halconResult dituo(std::shared_ptr data, cv::Mat& src, cv::Mat& Paint, } } for (int i = 0; i < contours.size(); i++) { - std::string ngDetails = (out_NgDetails[i].ToTuple().ToString().Text()); + std::string ngDetails = (output.result.out_NgDetails[i].ToTuple().ToString().Text()); ngDetails = gbkToUtf8(ngDetails.substr(1, ngDetails.length() - 2)); if (data->service->_paramSyncManager->get("底托检测", "底托偏移检测启用", false).toBool() == false && ngDetails == "底托偏移") return result; @@ -157,7 +190,7 @@ halconResult dituo(std::shared_ptr data, cv::Mat& src, cv::Mat& Paint, bool ALGO_BaoMoHouDiBu(std::shared_ptr data, baoMoHouDiBuDetParams* _detParams, yh_581_address address) { try { - + auto r = getOutputValue(20.5, 16.0, 18.0, 3); QDateTime createTime = QDateTime::currentDateTime(); qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; std::vector < std::string> defects; @@ -183,12 +216,12 @@ bool ALGO_BaoMoHouDiBu(std::shared_ptr data, baoMoHouDiBuDetParams* _d cv::Mat Paint, Paint1, Paint2; bool leftFirst = address.triggerAddr != 20200; - //cv::Mat first = data->getOriImg(0); - //cv::Mat second = data->getOriImg(1); - cv::Mat first = cv::imread("C:\\Users\\RCi\\Downloads\\dituo\\04QCB43581020PG1W0002024\\04QCB43581020PG1W0002024_Camera1_ORI_0_18-26-04-169.bmp", -1); - cv::Mat second = cv::imread("C:\\Users\\RCi\\Downloads\\dituo\\04QCB43581020PG1W0002024\\04QCB43581020PG1W0002024_Camera1_ORI_1_18-26-04-169.bmp", -1); - auto feature1 = std::async(std::launch::async, dituo, data, std::ref(first), std::ref(Paint1), leftFirst, std::ref(defects)); - auto feature2 = std::async(std::launch::async, dituo, data, std::ref(second), std::ref(Paint2), ~leftFirst, std::ref(defects)); + cv::Mat first = data->getOriImg(0); + cv::Mat second = data->getOriImg(1); + //cv::Mat first = cv::imread("C:\\Users\\RCi\\Downloads\\dituo\\04QCB43581020PG1W0002024\\04QCB43581020PG1W0002024_Camera1_ORI_0_18-26-04-169.bmp", -1); + //cv::Mat second = cv::imread("C:\\Users\\RCi\\Downloads\\dituo\\04QCB43581020PG1W0002024\\04QCB43581020PG1W0002024_Camera1_ORI_1_18-26-04-169.bmp", -1); + auto feature1 = std::async(std::launch::async, dituo, data, _detParams, std::ref(first), std::ref(Paint1), leftFirst, std::ref(defects)); + auto feature2 = std::async(std::launch::async, dituo, data, _detParams, std::ref(second), std::ref(Paint2), ~leftFirst, std::ref(defects)); feature1.wait(); halconResult hResult1 = feature1.get(); @@ -223,6 +256,7 @@ bool ALGO_BaoMoHouDiBu(std::shared_ptr data, baoMoHouDiBuDetParams* _d data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_" + QByteArray::number(hResult2.resultX));//发送结果 data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 6) + "_" + QByteArray::number(hResult2.resultY));//发送结果 data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult + ",0," + plcResult);//发送结果 + defects.push_back(std::to_string(clock())); data->service->_missionResultHandle->toBarChart(data->SNcode, defects); data->insertResultImg(Paint); return true; diff --git a/src/mission/old/581-BaoMo-Cam1213/581-BaoMo-Cam1213.h b/src/mission/old/581-BaoMo-Cam1213/581-BaoMo-Cam1213.h new file mode 100644 index 0000000..e84734b --- /dev/null +++ b/src/mission/old/581-BaoMo-Cam1213/581-BaoMo-Cam1213.h @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; +QMutex writeFileMutex; +QStringList headList({ "条码", "时间","位置","结果","缺陷明细" }); + +class baoMoHouDiBuDetParams { +public: + struct input { + HObject in_Image; + HTuple in_Par; + HTuple in_FilePath = "./halconFiles/"; + HTuple in_Resolution; + }; + struct output { + std::vector out_VectorX, out_VectorY; + HTuple out_Flag, out_NgDetails; + }; + halconEvaluater evaluater; + baoMoHouDiBuDetParams() { + evaluater.init("_4_TD_BlueCover_Inspection", + [](std::map& inputHObj, + std::map& inputHTuple, + const input& in) { + inputHObj["in_Image"] = in.in_Image; + inputHTuple["in_Par"] = in.in_Par; + inputHTuple["in_FilePath"] = in.in_FilePath; + inputHTuple["in_Resolution"] = in.in_Resolution; + }, + [](std::map& outputHObj, + std::map& outputHTuple, + std::map>& outputHTupleVector, + output& out) { + out.out_VectorX = outputHTupleVector["out_VectorX"]; + out.out_VectorY = outputHTupleVector["out_VectorY"]; + out.out_Flag = outputHTuple["out_Flag"]; + out.out_NgDetails = outputHTuple["out_NgDetails"]; + }); + } +}; +static bool ALGO_BaoMoHouDiBu(std::shared_ptr data, baoMoHouDiBuDetParams* _detParams, yh_581_address address); + +class _581_BaoMo : public mission { +public: + using baoMoHouDiBuTrigger = _581_Single_triggerTemplate; + baoMoHouDiBuTrigger _station_1; + baoMoHouDiBuTrigger _station_2; + + baoMoHouDiBuDetParams _baoMoHouDiBuDetParams; + baoMoHouDiBuDetParams _baoMoHouDiBuDetParams2; + //handleAiModelWithGuard testModel; + void onConfigReloaded() { + halconEngineManager::instance().loadAlgoFile("./halconAlgo/AlgoLibrary_581.hdpl", bridge); + //_serviceManager->_handleAiManager->loadSegModel("./handleAiModel/Seg_2026-3-25_1-05-47_2.130355.BHD", 16, 0, "铜Seg90度极耳定位网"); + //_serviceManager->_handleAiManager->loadSegModel("./handleAiModel/Seg_2026-3-25_1-05-47_2.130355.BHD", 16, 0, "铜Seg90度焊印定位网"); + } + bool initCustom() { +#pragma region 注册触发器回调 + + + _station_1.init("Camera1","包膜后底托位置度检测-左-相机12",20400,1,2, &_baoMoHouDiBuDetParams,_serviceManager,bridge, ALGO_BaoMoHouDiBu); + _station_2.init("Camera2", "包膜后底托位置度检测-右-相机13",20200,1,2,&_baoMoHouDiBuDetParams, _serviceManager, bridge, ALGO_BaoMoHouDiBu); +#pragma endregion + return true; + } + + + //9600 +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/old/581-BaoMo-Cam1213/CMakeLists.txt b/src/mission/old/581-BaoMo-Cam1213/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/old/581-BaoMo-Cam1213/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/581-BaoMo-Cam91011/581-BaoMo-Cam91011.cpp b/src/mission/old/581-BaoMo-Cam91011/581-BaoMo-Cam91011.cpp similarity index 97% rename from src/mission/581-BaoMo-Cam91011/581-BaoMo-Cam91011.cpp rename to src/mission/old/581-BaoMo-Cam91011/581-BaoMo-Cam91011.cpp index e0ee69d..421f5a5 100644 --- a/src/mission/581-BaoMo-Cam91011/581-BaoMo-Cam91011.cpp +++ b/src/mission/old/581-BaoMo-Cam91011/581-BaoMo-Cam91011.cpp @@ -174,8 +174,8 @@ void ExceptionResume(std::shared_ptr data, yh_581_address address, QStr if (Paint.channels() == 1) cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - TextRendererZH textPainter(Paint.cols, Paint.rows, 300); - textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cvPainter textPainter; + //textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); data->insertResultImg(Paint); //if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) @@ -186,7 +186,7 @@ void ExceptionResume(std::shared_ptr data, yh_581_address address, QStr resultCsv << QString::fromStdString(data->SNcode) << QDateTime::currentDateTime().toString("yyyyMMddhhmmss") << loaction << why << "异常"; //SaveLineToCSv("D:/CCDData/" + QDateTime::currentDateTime().toString("yyyyMMdd") + ".csv", resultCsv); } -halconResult halconAlgo1_BaoMoHouCeMian_HouDuAndChongHe(std::shared_ptr data, TextRendererZH* textPainter, baoMoHouDaMianDetParams* _detParams, yh_581_address address, cv::Mat& src) { +halconResult halconAlgo1_BaoMoHouCeMian_HouDuAndChongHe(std::shared_ptr data, cvPainter* textPainter, baoMoHouDaMianDetParams* _detParams, yh_581_address address, cv::Mat& src) { QElapsedTimer eTimer; eTimer.start(); bool isInsideCamera = QString::number(address.triggerAddr).startsWith("198") ? true : false; @@ -302,7 +302,7 @@ halconResult halconAlgo1_BaoMoHouCeMian_HouDuAndChongHe(std::shared_ptr data, TextRendererZH* textPainter, yh_581_address address, cv::Mat& src) { +halconResult halconAlgo1_BaoMoHouCeMian_HouDu(std::shared_ptr data, cvPainter* textPainter, yh_581_address address, cv::Mat& src) { QElapsedTimer eTimer; eTimer.start(); bool isInsideCamera = QString::number(address.triggerAddr).startsWith("198") ? true : false; @@ -372,7 +372,7 @@ halconResult halconAlgo1_BaoMoHouCeMian_HouDu(std::shared_ptr data, Te return hResult; } -bool halconAlgo1_BaoMoHouCeMian_PoSun(std::shared_ptr data, TextRendererZH* textPainter, TextRendererZH* textPainterCls, yh_581_address address, cv::Mat& src, cv::Mat& Paint, HObject& outImage, handdleAI::pClassification* clsPtr) { +bool halconAlgo1_BaoMoHouCeMian_PoSun(std::shared_ptr data, cvPainter* textPainter, cvPainter* textPainterCls, yh_581_address address, cv::Mat& src, cv::Mat& Paint, HObject& outImage, handdleAI::pClassification* clsPtr) { bool isOK = true; try { QElapsedTimer eTimer; @@ -466,8 +466,8 @@ bool ALGO_BaoMoHouCeMian(std::shared_ptr data, baoMoHouDaMianDetParams start = clock(); cv::Mat src = data->getOriImg(0); - TextRendererZH textPainter(Paint.cols, Paint.rows, 150); - TextRendererZH textPainterCls(Paint.cols, Paint.rows, 35); + cvPainter textPainter; + cvPainter textPainterCls; HObject outImage; std::future handleAiFuture1 = std::async(std::launch::async, halconAlgo1_BaoMoHouCeMian_PoSun, data, &textPainter, &textPainterCls, address, std::ref(src), std::ref(Paint), std::ref(outImage), _detParams->ceMian_cls); std::future handleAiFuture2; diff --git a/src/mission/581-BaoMo-Cam91011/581-BaoMo-Cam91011.h b/src/mission/old/581-BaoMo-Cam91011/581-BaoMo-Cam91011.h similarity index 100% rename from src/mission/581-BaoMo-Cam91011/581-BaoMo-Cam91011.h rename to src/mission/old/581-BaoMo-Cam91011/581-BaoMo-Cam91011.h diff --git a/src/mission/old/581-BaoMo-Cam91011/CMakeLists.txt b/src/mission/old/581-BaoMo-Cam91011/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/old/581-BaoMo-Cam91011/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/581-Dgh-CB-1/581-Dgh-CB-1.cpp b/src/mission/old/581-Dgh-CB-1/581-Dgh-CB-1.cpp similarity index 100% rename from src/mission/581-Dgh-CB-1/581-Dgh-CB-1.cpp rename to src/mission/old/581-Dgh-CB-1/581-Dgh-CB-1.cpp diff --git a/src/mission/581-Dgh-CB-1/581-Dgh-CB-1.h b/src/mission/old/581-Dgh-CB-1/581-Dgh-CB-1.h similarity index 99% rename from src/mission/581-Dgh-CB-1/581-Dgh-CB-1.h rename to src/mission/old/581-Dgh-CB-1/581-Dgh-CB-1.h index 9b985b4..07756b8 100644 --- a/src/mission/581-Dgh-CB-1/581-Dgh-CB-1.h +++ b/src/mission/old/581-Dgh-CB-1/581-Dgh-CB-1.h @@ -81,7 +81,7 @@ static bool yuHanQian3D(std::shared_ptr data, detParams* _detParams, y HTuple out_DisplayRows = procedureCall.GetOutputCtrlParamTuple("out_DisplayRows"); HTuple out_DisplayCols = procedureCall.GetOutputCtrlParamTuple("out_DisplayCols"); int flag = out_Flag.D(); - TextRendererZH textPainter(Paint.cols, Paint.rows, 75); + cvPainter textPainter; textPainter.appendPaintJob(data->SNcode, cv::Point(1600,0), cv::Scalar(0, 255, 0)); textPainter.appendPaintJob(data->cameraKeyName, cv::Point(1600, 50), cv::Scalar(0, 255, 0)); diff --git a/src/mission/old/581-Dgh-CB-1/CMakeLists.txt b/src/mission/old/581-Dgh-CB-1/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/old/581-Dgh-CB-1/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/old/581-Dgh-TSBH/581-Dgh-TSBH.cpp b/src/mission/old/581-Dgh-TSBH/581-Dgh-TSBH.cpp new file mode 100644 index 0000000..3853dd3 --- /dev/null +++ b/src/mission/old/581-Dgh-TSBH/581-Dgh-TSBH.cpp @@ -0,0 +1,8 @@ +#include <581-Dgh-TSBH.h> + +mission* createMission() { + return new _581_Dgh(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/old/581-Dgh-TSBH/581-Dgh-TSBH.h b/src/mission/old/581-Dgh-TSBH/581-Dgh-TSBH.h new file mode 100644 index 0000000..67931d1 --- /dev/null +++ b/src/mission/old/581-Dgh-TSBH/581-Dgh-TSBH.h @@ -0,0 +1,592 @@ +#include +#include +#include +#include +#include +using namespace HDevEngineCpp; + +template +//此类适用于单相机多触发地址单次判断的情况,比如密封钉,三协机台 +//例触发地址有4个,输入信号为 1 0 0 1 则按顺序触发1和4的地址拍照检测,2和3不做处理 +class _581_Single_TSBH_triggerTemplate { +public: + paramTemplte2* _detParams; + detCallbackChain* generalDet; + std::string cameraName; + QString viewerName; + std::mapaddress; + int oneTriggerCaptrueTimes = 1; + /// + /// 初始化 + /// + /// 相机名 + /// 窗口名 + /// 开始地址 + /// 触发信号数 + /// 单次触发调用几次相机模块 + /// 参数输入 + /// 默认 + /// 默认 + /// 算法函数 + /// + void init(std::string cameraNameIn, QString viewerNameIN, int startAddr, int triggerCount, int oneTriggerCaptrueTimesIN, + paramTemplte2* detParamsIn, ServiceManager* _serviceManagerIn, bSignalHub* bridgeIn, + typename detCallbackChain::processAlgoCallback _processAlgoCallback, + QString saveObjectNameIN = "") { + viewerName = viewerNameIN; + cameraName = cameraNameIn; + _serviceManager = _serviceManagerIn; + _detParams = detParamsIn; + bridge = bridgeIn; + oneTriggerCaptrueTimes = oneTriggerCaptrueTimesIN; + + saveObjectName = saveObjectNameIN; + generalDet = new detCallbackChain(_serviceManager, _detParams, bridge); + generalDet->registerProcessAlgoCallback(_processAlgoCallback); + address = generateYh581AddressMap(startAddr, triggerCount); + //单次触发器条件 + for (int i = 0; i < triggerCount; i++) { + //注册单触发器 触发地址置1时触发 + _serviceManager->_triggerManager->registerCallback(address[i].triggerAddr, 1, [this, i](int triggerPos, int triggerValue, QByteArray& byteArray) { + evaluate(i, byteArray); }, ""); + //注册清除完成信号触发器 任意触发地址归零时触发一次 + _serviceManager->_triggerManager->registerCallback(address[i].triggerAddr, 0, [this, i](int triggerPos, int triggerValue, QByteArray& byteArray) { + _serviceManager->_triggerManager->writeShort("D" + address[i].completeAddr + "_0"); }, "D" + address[0].completeAddr.toStdString() + "清零"); + } + for (int i = 0; i < address.size(); i++) { + _serviceManager->_triggerManager->registerPlcValueViewer(viewerNameIN + "_" + QString::number(i), + { {QString::number(address[i].triggerAddr) + "触发",address[i].triggerAddr,finsShort }, + {address[i].completeAddr + "反馈",address[i].completeAddr.toInt(),finsShort}, + {address[i].resultAddr + "结果",address[i].resultAddr.toInt(),finsShort}, + {QString::number(address[i].valueOffsetAddr) + "数值1",address[i].valueOffsetAddr,finsFloat}, + {QString::number(address[i].valueOffsetAddr + 2) + "数值2",address[i].valueOffsetAddr + 2,finsFloat}, + {QString::number(address[i].valueOffsetAddr + 4) + "数值3",address[i].valueOffsetAddr + 4,finsFloat} }); + } + + } + //获取图片并进行检测 + void evaluate(int batteryOffset, QByteArray& byteArray) { + std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, -1); + auto camera = _serviceManager->_cameraManager->getCamera(cameraName); + camera->clearCache(); + ////TSB新增,不同的触发地址拍摄不同张数的图片 + int captureImgCount = 1; + if (address[batteryOffset].triggerAddr == 18000 || + address[batteryOffset].triggerAddr == 18002 || + address[batteryOffset].triggerAddr == 18004 || + address[batteryOffset].triggerAddr == 18006) {//2D相机 + captureImgCount = 2; + } + else if (address[batteryOffset].triggerAddr == 18000 || + address[batteryOffset].triggerAddr == 18002 || + address[batteryOffset].triggerAddr == 18004 || + address[batteryOffset].triggerAddr == 18006) {//3D相机 + captureImgCount = 4; + } + std::shared_ptr data = _serviceManager->_detDataManager->createDetData( + QString::fromStdString(cameraName), sn, captureImgCount, camera->config.oriSaveFormat);//创建数据包 + ///END + data->saveObjectName = saveObjectName; + data->resultImgShowSet.push_back(showImgPack(viewerName, "结果图")); + camera->captureImage(data);//拍照 + + generalDet->asyncProcess(data, address[batteryOffset]);//处理算法 + _serviceManager->_triggerManager->writeShort("D" + address[batteryOffset].completeAddr + "_1");//完成信号 根据偏移发送 + } +private: + QString saveObjectName = ""; + bSignalHub* bridge; + ServiceManager* _serviceManager = nullptr; +}; + +#pragma region 辊压后2D算法 + +struct GunYaModels { + handdleAI::pSegmentation* seg = nullptr; + handdleAI::pClassification* cls_step1 = nullptr; + handdleAI::pClassification* cls_step2 = nullptr; + handdleAI::pDetection* det = nullptr; +}; +class GunYaHou2DParam { +public: + std::vector< GunYaModels> models; +}; +void exceptionResume(std::shared_ptr data, GunYaHou2DParam* _detParams, yh_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_1");//数值1 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_1");//数值2 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_1");//数值3 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 6) + "_1");//数值4 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 8) + "_1");//数值5 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 10) + "_1");//数值6 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 12) + "_1");//数值7 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 14) + "_1");//数值8 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 16) + "_1");//数值9 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 +} +cv::Mat LocalCutImg_DET(handdleAI::pDetection* pDet, cv::Mat src, float zoom, int checkNameId, cv::Rect& cutRoi) +{ + cv::Mat dst; + cv::resize(src, dst, cv::Size(src.cols / zoom, src.rows / zoom)); + std::vector resultBox = pDet->Evaluate(dst); //结果为中心点+宽高 + + cv::Mat contourImg; + std::vector> contours; + //cv::threshold(dstImg, contourImg, pSeg->m_classes, 255, cv::THRESH_BINARY_INV); + //cv::findContours(contourImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + + cv::Mat roiImg; + + if (resultBox.size() <= 0) + { + cv::Mat rstMat = cv::Mat(100, 100, CV_8UC1, cv::Scalar(0, 0, 0)); + cutRoi = cv::Rect(0, 0, 100, 100); + return rstMat; + } + + double maxArea = 0; + int index = -1; + for (int j = 0; j < resultBox.size(); j++) + { + double area = resultBox[j].w * resultBox[j].h; + if (area >= maxArea) + { + maxArea = area; + index = j; + } + } + + /*cv::Rect rect = cv::boundingRect(contours[index]); + cv::rectangle(roiImg, cv::Rect(resultBox[j].x - resultBox[j].w / 2, resultBox[j].y - resultBox[j].h / 2, resultBox[j].w, resultBox[j].h), cv::Scalar(0, 0, 255), 5);*/ + int addpixel = 15; + + cutRoi.x = ((resultBox[index].x - resultBox[index].w / 2) * zoom); + cutRoi.y = ((resultBox[index].y - resultBox[index].h / 2) * zoom); + cutRoi.width = (resultBox[index].w * zoom); + cutRoi.height = (resultBox[index].h * zoom); + //cv::rectangle(src, cutRoi, cv::Scalar(0, 0, 255), 2, 8); + /* cutRoi.x = rect.x * zoom; + cutRoi.y = rect.y * zoom; + cutRoi.width = rect.width * zoom; + cutRoi.height = rect.height * zoom;*/ + + src(cutRoi).copyTo(roiImg); + return roiImg; +} +bool handleAiEvaluate(std::shared_ptr data, GunYaModels* models, cv::Mat& srcImg, cv::Mat& showImg) { + bool handleAiResultIsNG = false; + clock_t start = clock(); + + + //cv::rectangle(showImg, detRect, cv::Scalar(0, 255, 255), 2); + //cv::Mat segImg = ~_detParams->seg->Evaluate(det_Image);//一定要取反,因为seg默认抓到的区域为黑色 + //data->segOriginImgs.push_back(det_Image); + cv::Mat det_Image = srcImg; + data->insertSegOriginImg(det_Image); + cv::Mat segImg = ~models->seg->Evaluate(det_Image);//一定要取反,因为seg默认抓到的区域为黑色 + data->insertSegOriginImg(segImg); + clock_t stop = clock(); + std::vector>contours;//点集 + stop = clock(); + cv::findContours(segImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); // 寻找白色区域并储存为点集 + //QMutexLocker lock(showImgLocker);//加锁防止和halcon线程冲突 + int clsResult = 0; + uint OrWidthMin = data->service->_paramSyncManager->get("2D设置", "最小宽筛选(OR)(pix)", 100).toUInt(); + uint OrHeightMax = data->service->_paramSyncManager->get("2D设置", "最小高筛选(OR)(pix)", 40).toUInt(); + uint AndWidthMin = data->service->_paramSyncManager->get("2D设置", "最小宽筛选(AND)(pix)", 30).toUInt(); + uint AndHeightMax = data->service->_paramSyncManager->get("2D设置", "最小高筛选(AND)(pix)", 10).toUInt(); + double maxProbThreshold = data->service->_paramSyncManager->get("2D设置", "最小置信度Prob", 0.5).toDouble(); + int externalSize = data->service->_paramSyncManager->get("2D设置", "cls裁切外扩大小Pix", 0).toInt(); + std::vector> clsSavImg; + cvPainter clsTextPainter; + for (int i = 0; i < contours.size(); i++)//遍历点集 + { + cv::Rect rect = cv::boundingRect(contours[i]);//最小外接矩形 + //if( (rect.height > 25 || rect.width > 50) && (rect.height > 10 && rect.width > 10))// 过滤 + if ((rect.height > OrHeightMax || rect.width > OrWidthMin) && (rect.height > AndHeightMax && rect.width > AndWidthMin)) + { + + rect.x -= externalSize; rect.y -= externalSize; rect.width -= externalSize; rect.height -= externalSize; + auto clsimgs = cutImg2ClsVec(rect, det_Image); + bool isNg = false; + std::string ngDefectText,okDefectText; + for (auto& clsImg : clsimgs)//对一个单独缺陷切割多张图片 + { + cv::Scalar color; + std::string defectText; + clsResult = models->cls_step1->Evaluate(clsImg);//cls检测 得出的是int型 从0开始 + double prob = models->cls_step1->getProbability(); + if (clsResult == 3 && prob >= maxProbThreshold) { + int clsResult_step2 = models->cls_step2->Evaluate(clsImg); + double prob_step2 = models->cls_step2->Evaluate(clsImg); + if (clsResult_step2 == 1 && prob_step2 >= maxProbThreshold) { + isNg = true; + color = cv::Scalar(0, 0, 255); + defectText = "炸焊r" + std::to_string(clsResult) + "_p" + std::to_string(prob) + "_r2" + std::to_string(clsResult_step2) + "_p2" + std::to_string(prob_step2); + } + else { + color = cv::Scalar(0, 255, 0); + defectText = "焊烟r" + std::to_string(clsResult) + "_p" + std::to_string(prob); + } + } + else { + color = cv::Scalar(0, 255, 0); + if (clsResult == 0) { + defectText = "OK_r" + std::to_string(clsResult) + "_p" + std::to_string(prob); + } + else if (clsResult == 1) { + defectText = "轨迹偏移r" + std::to_string(clsResult) + "_p" + std::to_string(prob); + } + else if (clsResult == 2) { + defectText = "鱼鳞纹r" + std::to_string(clsResult) + "_p" + std::to_string(prob); + } + else { + defectText = "r" + std::to_string(clsResult) + "_p" + std::to_string(prob); + } + } + + if (isNg == true) + ngDefectText = defectText; + else + okDefectText = defectText; + clsSavImg.push_back({ defectText,clsImg }); + } + + + if (isNg == true){//NG + rectangle(showImg, cv::Rect{ rect.x,rect.y,rect.width ,rect.height }, cv::Scalar(0, 0, 255), 2, 8, 0); + drawContours(showImg, contours, i, cv::Scalar(0, 0, 255), 1); + clsTextPainter.appendPaintJob(ngDefectText, cv::Point(rect.x - 25, rect.y), cv::Scalar(0, 0, 255)); + handleAiResultIsNG = true; + } + else{//OK + rectangle(showImg, cv::Rect{ rect.x,rect.y,rect.width ,rect.height }, cv::Scalar(0, 255, 255), 2, 8, 0); + drawContours(showImg, contours, i, cv::Scalar(0, 255, 0), 1); + clsTextPainter.appendPaintJob(okDefectText, cv::Point(rect.x - 25, rect.y), cv::Scalar(0, 255, 0)); + } + clsTextPainter.executePaintJobs(showImg); + { + cv::Mat testClsImg; + cv::resize(det_Image(rect), testClsImg, cv::Size(128, 128)); + int clsResult = models->cls_step1->Evaluate(testClsImg); + clsSavImg.push_back({ "Func2Result" + std::to_string(clsResult) + "_prob" + std::to_string(models->cls_step1->getProbability()),testClsImg }); + } + } + else + { + auto clsimgs = cutImg2ClsVec(rect, det_Image); + int cnt = 0; + for (auto& clsImg : clsimgs) + { + data->insertClsOriginImg({ std::make_pair(std::to_string(clsResult),clsImg) }); + //addImgToSaveStruct(clsImg, "0", savePath_sorce + "/CSSMALL_" + SN + "_" + sdataTime_name + ".bmp"); + cnt++; + drawContours(showImg, contours, i, cv::Scalar(0, 255, 0), 1); + } + } + } + data->insertClsOriginImg(clsSavImg); + return handleAiResultIsNG; +} + +static bool GunYaHou2D(std::shared_ptr data, GunYaHou2DParam* _detParams, yh_581_address address) { + try { + QDateTime createTime = QDateTime::currentDateTime(); + std::vectordefects; + //data->deleteOriImg(0); + //data->deleteOriImg(0); + //data->deleteOriImg(0); + //data->deleteOriImg(0); + //data->deleteOriImg(0); + //data->insertOriImg(cv::imread("D://testImage//gunya_NG.bmp", -1)); + //data->insertOriImg(cv::imread("D://testImage//gunya_NG.bmp", -1)); + //data->insertOriImg(cv::imread("D://testImage//gunya_NG.bmp", -1)); + //data->insertOriImg(cv::imread("D://testImage//gunya_NG.bmp", -1)); + //data->insertOriImg(cv::imread("D://testImage//gunya_NG.bmp", -1)); + //data->insertOriImg(cv::imread("D://testImage//gunya_NG.bmp", -1)); + int imgSize = data->getOriImgSize(); + cv::Mat PaintSum; + std::vectorPaints; + GunYaModels* models; + if (address.triggerAddr == 20400 || address.triggerAddr == 20400 || address.triggerAddr == 20600 || address.triggerAddr == 20800) + models = &_detParams->models[0]; + else + models = &_detParams->models[1]; + + bool isNg = false; + for (int i = 0; i < data->getOriImgSize(); i++) { + cv::Mat sourceImg = data->getOriImgVec().at(i), Paint, detImg; + cv::resize(sourceImg, detImg, cv::Size(), 0.75, 0.75); + detImg(cv::Rect(0, 150, detImg.cols, detImg.rows - 400)).copyTo(detImg); + detImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + bool loopIsNg = handleAiEvaluate(data, models, detImg, Paint); + Paints.push_back(Paint); + TextRendererZH textPainter(Paint.cols, Paint.rows, 150); + textPainter.putText(Paint, loopIsNg == false ? "OK" : "NG", cv::Point(0, 0), loopIsNg == false ? cv::Scalar(0, 255, 0) : cv::Scalar(0, 0, 255)); + if (loopIsNg == true) + isNg = true; + } + QByteArray plcResult = "1"; + if (isNg == true) { + plcResult = "2"; + data->aiDetOK = false; + defects.push_back("焊后NG"); + } + + if(!data->service->_paramSyncManager->get("2D设置", "2D启用", true).toBool()) + plcResult = "1"; + if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + plcResult = "1"; + Paints[0].copyTo(PaintSum); + for (int i = 1; i < Paints.size(); i++) + cv::vconcat(Paints[i], PaintSum, PaintSum); + + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_1,1,1,1,1,1,1,1,1");//数值1 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + int rate = MIN(PaintSum.rows, PaintSum.cols) / data->service->_paramSyncManager->get("通用设置","辊压后2D时间打印字体缩放倍率",40).toInt(); + TextRendererZH textPainter1(PaintSum.cols, PaintSum.rows, rate); + textPainter1.putText(PaintSum, data->SNcode + " " + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, PaintSum.rows - rate), cv::Scalar(0, 255, 255)); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects); + data->insertResultImg(PaintSum); + return true; + } + CATCH_EXCEPTIONS + exceptionResume(data, _detParams, address, "算法异常"); + return true; +} + +#pragma endregion +static bool generalAlgoTestDuanBian(std::shared_ptr data, detParams* _detParams, yh_581_address address) { + try { + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + data->customPath = "{imgSaveRootDir}/{yyyy-MM-dd}/{saveObjectName}{imageType}/{HH}H/{processResult}/"; + std::vector < std::string> defects = { "测试" }; + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() < 1) + { + qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); + return false; + } + if (data->getOriImg(0).rows == 100) { + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); + return false; + } + + cv::Mat Paint; + QMutex paintMutex; + clock_t start, finish; + start = clock(); + cv::Mat src1 = data->getOriImg(0); + if (src1.channels() != 3) + cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); + else + src1.copyTo(Paint); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 15); + textPainter.putText(Paint, "划伤", cv::Point(10, 10), cv::Scalar(0, 255, 0)); + cv::putText(Paint, "pos:" + data->additional["触发地址"].toString().toStdString(), cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_1,1,1,1");//数值1 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + data->service->_missionResultHandle->toBarChart(data->SNcode, defects);//发送图标操作 + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + return true; + } + CATCH_EXCEPTIONS + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_1,1,1,1");//数值1 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送异常结果 + return false; +} +void _3DExceptionResume(std::shared_ptr data, yh_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->insertResultImg(Paint); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_1,1,1,1,-99,-99");//数值1 + //if (data->service->_paramSyncManager->get("通用设置", "强制OK", false).toBool()) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + //else + // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 +} +static bool gunYaHou3D(std::shared_ptr data, detParams* _detParams, yh_581_address address) { + try { + /* double heightThreshold = data->service->_paramSyncManager->get("预焊前", "高度/台阶差最大阈值", 0.3).toDouble(); + double widthThreshold = data->service->_paramSyncManager->get("预焊前", "缝隙值最大阈值", 0.1).toDouble();*/ + //data->oriImgs.clear(); + //data->oriImgs.push_back(cv::imread("D://testImage//Camera1.bmp", -1)); + //data->oriImgs.push_back(cv::imread("D://testImage//Camera1.tiff", -1)); + std::vectordefects; + if (data->getOriImgSize() < 2) { + _3DExceptionResume(data, address, "NG"); + return true; + } + QDateTime createTime = QDateTime::currentDateTime(); + cv::Mat heightImg = data->getOriImg(1); + cv::Mat lumImg = data->getOriImg(0); + cv::Mat Paint; + + if (lumImg.channels() != 3) + cv::cvtColor(lumImg, Paint, cv::COLOR_GRAY2BGR); + else + lumImg.copyTo(Paint); + + HalconCpp::HObject in_ImageG = MatToHObject(lumImg); + HalconCpp::HObject in_ImageH = MatToHObject(heightImg); + HDevEngineCpp::HDevProcedure proc("_6_Cal_LB_Hump_Height"); + HDevProcedureCall procedureCall(proc); + procedureCall.SetInputIconicParamObject("in_ImageH", in_ImageH); + procedureCall.SetInputIconicParamObject("in_ImageG", in_ImageG); + + if(data->cameraKeyName == "Camera9" || data->cameraKeyName == "Camera11") + procedureCall.SetInputCtrlParamTuple("in_ProductPosition", 1); + else + procedureCall.SetInputCtrlParamTuple("in_ProductPosition", 2); + + procedureCall.SetInputCtrlParamTuple("in_xResolution", 0.005); + procedureCall.SetInputCtrlParamTuple("in_yResolution", data->service->_paramSyncManager->get("3D设置", "3D相机Y方向精度", 0.067).toFloat()); + procedureCall.SetInputCtrlParamTuple("in_zResolution", 0.0008); + procedureCall.Execute(); + + HTuple out_Height = procedureCall.GetOutputCtrlParamTuple("out_Height"); + HTuple out_X = procedureCall.GetOutputCtrlParamTuple("out_X"); + HTuple out_Y = procedureCall.GetOutputCtrlParamTuple("out_Y"); + HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); + HTuple out_NgDetails = procedureCall.GetOutputCtrlParamTuple("out_NgDetails"); + + int flag = out_Flag.D(); + double dingMianTuGao = -99, ceMianTuGao = -99; + TextRendererZH textPainter(Paint.cols, Paint.rows, 100); + textPainter.putText(Paint, data->SNcode, cv::Point(1600, 50), cv::Scalar(0, 255, 0)); + QByteArray textSend; + float CeMin = data->service->_paramSyncManager->get("3D设置", "侧面凸高最小值", 0).toFloat(); + float CeMax = data->service->_paramSyncManager->get("3D设置", "侧面凸高最大值", 0.3).toFloat(); + float DingMin = data->service->_paramSyncManager->get("3D设置", "顶面凸高最小值", 0.0).toFloat(); + float DingMax = data->service->_paramSyncManager->get("3D设置", "顶面凸高最大值", 0.25).toFloat(); + bool ceMianEnable = data->service->_paramSyncManager->get("3D设置", "侧面凸高检测启用", true).toBool(); + bool dingMianEnable = data->service->_paramSyncManager->get("3D设置", "顶面凸高检测启用", true).toBool(); + QByteArray plcResult = "1"; + if (flag == 1) { + ceMianTuGao = out_Height[0].D(); + dingMianTuGao = out_Height[1].D(); + + + for (int i = 0; i < out_X.Length() && i < out_Y.Length(); i++) { + cv::circle(Paint, cv::Point(out_X[i].D(), out_Y[i].D()), 10, cv::Scalar(0, 0, 255), 3); + } + if (std::abs(ceMianTuGao) > CeMax || std::abs(ceMianTuGao) < CeMin ){//NG + textPainter.putText(Paint, "侧面凸高:" + std::to_string(ceMianTuGao), cv::Point(50, 150), cv::Scalar(0, 0, 255)); + defects.push_back("侧面凸高NG"); + data->halconOK = false; + if (ceMianEnable) { + plcResult = "2"; + } + } + else { + textPainter.putText(Paint, "侧面凸高:" + std::to_string(ceMianTuGao), cv::Point(50, 150), cv::Scalar(0, 255, 0)); + } + + if (std::abs(dingMianTuGao) > DingMax || std::abs(dingMianTuGao) < DingMin){//NG + textPainter.putText(Paint, "顶面凸高:" + std::to_string(dingMianTuGao), cv::Point(50, 250), cv::Scalar(0, 0, 255)); + defects.push_back("顶面凸高NG"); + data->halconOK = false; + if (dingMianEnable) { + plcResult = "2"; + } + } + else { + textPainter.putText(Paint, "顶面凸高:" + std::to_string(dingMianTuGao), cv::Point(50, 250), cv::Scalar(0, 255, 0)); + } + //data->halconOK = false; + //isOK = false; + } + else { + defects.push_back("辊压后3D算法NG"); + textPainter.putText(Paint, gbkToUtf8(out_NgDetails.ToString().Text()), cv::Point(50, 150), cv::Scalar(0, 0, 255)); + data->halconOK = false; + plcResult = "2"; + } + + + if (plcResult == "2") { + textPainter.putText(Paint, "NG", cv::Point(50, 50), cv::Scalar(0, 0, 255)); + data->halconOK = false; + } + else + textPainter.putText(Paint, "OK", cv::Point(50, 50), cv::Scalar(0, 255, 0)); + + plcResult = "1"; + int rate = MIN(Paint.rows, Paint.cols) / data->service->_paramSyncManager->get("通用设置", "辊压后3D时间打印字体缩放倍率", 20).toInt(); + TextRendererZH textPainter1(Paint.cols, Paint.rows, rate); + textPainter1.putText(Paint, data->SNcode + " " + createTime.toString("yyyy/MM/dd hh:mm:ss").toStdString(), cv::Point(50, Paint.rows - rate), cv::Scalar(0, 255, 255)); + + data->insertResultImg(Paint); + data->service->_triggerManager->writeFloat("D" + + QByteArray::number(address.valueOffsetAddr) + "_" + + "1,1,1,1," + QByteArray::number(dingMianTuGao) + "," + QByteArray::number(ceMianTuGao));//数值1 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + return true; + } + CATCH_EXCEPTIONS + _3DExceptionResume(data, address, "NG"); + return true; +} +class _581_Dgh : public mission { + +public: + _581_Single_TSBH_triggerTemplate dgh_TSBH_2D; + _581_Single_TSBH_triggerTemplate dgh_TSBH_3D; + + GunYaHou2DParam _GunYaHou2DParam; + detParams _params; + bool initOnce = false; + void onConfigReloaded() { + HDevEngineCpp::HDevEngine myEngine; + myEngine.SetProcedurePath("./halconAlgo/AlgoLibrary_602.hdpl"); + if (initOnce == false) { + _GunYaHou2DParam.models = std::vector< GunYaModels>(2); + for (auto& model : _GunYaHou2DParam.models) { + model.seg = handdleAI::CreateSegObjS("./handleaiModels/seg_hanhou.BHD", 16,0); + model.cls_step1 = handdleAI::CreateClsObjV3("./handleaiModels/cls_hanhou_step1.BHD", 0, 1); + model.cls_step2 = handdleAI::CreateClsObjV3("./handleaiModels/cls_hanhou_step2.BHD", 0, 1); + //param.det = handdleAI::CreateDetObjS("./handleaiModels/det_hanhou.BHD", 1, 0); + } + initOnce = true; + } + } + bool initCustom() { +#pragma region 注册触发器回调 满焊前2D + + dgh_TSBH_2D.init("Camera5", "填丝补焊2D", 18000, 2,5, &_GunYaHou2DParam, _serviceManager, bridge, GunYaHou2D, "填丝补焊2D"); + dgh_TSBH_2D.address[1].valueOffsetAddr += 20; + + dgh_TSBH_3D.init("Camera9", "填丝补焊3D", 18200, 2,1, &_params, _serviceManager, bridge, gunYaHou3D, "填丝补焊3D"); + dgh_TSBH_3D.address[1].valueOffsetAddr += 20; +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/old/581-Dgh-TSBH/CMakeLists.txt b/src/mission/old/581-Dgh-TSBH/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/old/581-Dgh-TSBH/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/581-Mfd_yh_mh_hh/581-Mfd.cpp b/src/mission/old/581-Mfd_yh_mh_hh/581-Mfd.cpp similarity index 100% rename from src/mission/581-Mfd_yh_mh_hh/581-Mfd.cpp rename to src/mission/old/581-Mfd_yh_mh_hh/581-Mfd.cpp diff --git a/src/mission/old/581-Mfd_yh_mh_hh/581-Mfd.h b/src/mission/old/581-Mfd_yh_mh_hh/581-Mfd.h new file mode 100644 index 0000000..d8b99e4 --- /dev/null +++ b/src/mission/old/581-Mfd_yh_mh_hh/581-Mfd.h @@ -0,0 +1,783 @@ +#include +#include + +#pragma region 算法 +#include +#include +#include + +class mfd_hh_models { +public: + handdleAI::pSegmentation* seg_hanHou = nullptr; + handdleAI::pClassification* cls_hanHou = nullptr; + handdleAI::pSegmentation* seg_hanHouDingWei = nullptr; +}; +class mfd_detParams { +public: + int biaoding = 0; +}; +class mfdHanHouParam { +public: + + std::vector< mfd_hh_models>models = std::vector< mfd_hh_models>(4); + ~mfdHanHouParam() { + delete mutex; + } + + int waitMs = 4000; + void insertImg(cv::Mat& img, int index) { + QMutexLocker locker(mutex); + cv::Mat tmp; + img.copyTo(tmp); + mats.insert(index, tmp);//发送检测数据到主线程 + } + bool waitImage(cv::Mat& out, int index) { + int cnt = 0; + while (!mats.contains(index) && cnt < waitMs) { + QThread::msleep(100); + cnt += 100; + } + QMutexLocker locker(mutex); + if (mats.contains(index)) { + out = mats.find(index).value(); + mats.remove(index); + std::cout << "get" << std::endl; + return true; + } + std::cout << "overTime" << std::endl; + return false; + } + void clear() { + QMutexLocker locker(mutex); + mats.clear(); + results.clear(); + } + + void insertResult(std::string sn, bool iOK) { + QMutexLocker locker(mutex); + //if (mats.size() > 0) { + // qWarning() << "焊后有遗留的图像,已清除"; + // mats.clear(); + //} + results.insert(sn, iOK);//发送检测数据到主线程 + } + bool waitResult(std::string sn) { + int cnt = 0; + while (!results.contains(sn) && cnt < waitMs * 2) { + QThread::msleep(100); + cnt += 100; + } + QMutexLocker locker(mutex); + if (results.contains(sn)) { + qDebug() << "resultGet"; + return results[sn]; + } + qDebug() << "resultOverTime"; + return false; + } +private: + QMap mats; + QMap results; + QMutex* mutex = new QMutex(); +}; +using namespace HDevEngineCpp; +void putSnAndDetTime(cv::Mat Paint, const std::string& sn, int textSize = 30, const std::string& exteraText = "") +{ + TextRendererZH textPainter(Paint.cols, Paint.rows, textSize); + textPainter.putText(Paint, "SN:" + sn + exteraText, cv::Point(0, Paint.rows - textSize * 2), cv::Scalar(0, 255, 0)); + textPainter.putText(Paint, "TIME:" + QDateTime::currentDateTime().toString().toStdString() + exteraText, cv::Point(0, Paint.rows - textSize), cv::Scalar(0, 255, 0)); +} +void exceptionResume(std::shared_ptr data, yh_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + TextRendererZH textPainter(Paint.cols, Paint.rows, 100); + textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + putSnAndDetTime(Paint, data->SNcode, 50); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2,0.01,0.01");//结果反馈 + //data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.01");//结果反馈 + //data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.01");//结果反馈 +} +void exceptionResumeHH(std::shared_ptr data, yh_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->getOriImg(0).copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + TextRendererZH textPainter(Paint.cols, Paint.rows, 100); + textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + putSnAndDetTime(Paint, data->SNcode, 50); + data->insertResultImg(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 +} +static bool ManHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address) { + try { + + std::map sqlMapData; + sqlMapData["项目"] = "满焊"; + sqlMapData["相机"] = "相机" + address.triggerAddr; + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() < 1) + { + exceptionResume(data, address, "图片数量不满足要求"); + qDebug() << "图片数量不满足要求:" << data->getOriImgSize(); + return false; + } + + // QDateTime currentimenow = QDateTime::currentDateTime(); + cv::Mat Paint; + cv::Mat src1 = data->getOriImg(0); + //src1 = cv::imread("D:\\密封钉CCD存图\\2025-12-14\\满焊CCD原图\\10H\\OK\\相机3孔5\\A100000485_Camera5_0_10-12-45.bmp", -1); + //src1 = cv::imread("D:\\04QCB82602000JG1300507BN_Camera3_ORI_0_04-15-03-197.bmp", -1); + if (src1.channels() != 3) + cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); + else + src1.copyTo(Paint); + //处理算法 +#pragma region halcon处理 + std::string cameraNumberStr = "1", FixtureNumber = "1"; + + if (address.triggerAddr == 20000) { + cameraNumberStr = "1"; + FixtureNumber = "1"; + } + else if (address.triggerAddr == 20002) { + cameraNumberStr = "1"; + FixtureNumber = "2"; + } + else if (address.triggerAddr == 20200) { + cameraNumberStr = "2"; + FixtureNumber = "3"; + } + else if (address.triggerAddr == 20202) { + cameraNumberStr = "2"; + FixtureNumber = "4"; + } + else if (address.triggerAddr == 20400) { + cameraNumberStr = "3"; + FixtureNumber = "5"; + } + else if (address.triggerAddr == 20402) { + cameraNumberStr = "3"; + FixtureNumber = "6"; + } + else if (address.triggerAddr == 20600) { + cameraNumberStr = "4"; + FixtureNumber = "7"; + } + else if (address.triggerAddr == 20602) { + cameraNumberStr = "4"; + FixtureNumber = "8"; + } + data->customPath = "{imgSaveRootDir}/{yyyy-MM-dd}/{saveObjectName}{imageType}/{HH}H/{processResult}/相机" + QString::fromStdString(cameraNumberStr) + "孔" + QString::fromStdString(FixtureNumber); + int ManHanbiaoding_Switch = data->service->_paramSyncManager->get("满焊", "模式选择:1标定0生产", 0).toInt(); + if (ManHanbiaoding_Switch == 1) + data->service->_paramSyncManager->edit("满焊", "模式选择:1标定0生产", 0); + HalconCpp::HObject in_Image = MatToHObject(src1); + + HDevEngineCpp::HDevProcedure proc("_9_manhan"); + HDevProcedureCall procedureCall(proc); + procedureCall.SetInputIconicParamObject("in_Image", in_Image); + procedureCall.SetInputCtrlParamTuple("in_ProductionType", ManHanbiaoding_Switch); + procedureCall.SetInputCtrlParamTuple("in_CameraNumber", HTuple(cameraNumberStr.c_str())); + procedureCall.SetInputCtrlParamTuple("in_FixtureNumber", HTuple(FixtureNumber.c_str())); + procedureCall.SetInputCtrlParamTuple("in_FilePath1", HTuple(("./halconFiles/template_manHan/" + data->cameraKeyName + "/").c_str())); + procedureCall.SetInputCtrlParamTuple("in_FilePath2", HTuple("./halconFiles/base_manHan/")); + procedureCall.SetInputCtrlParamTuple("in_LineType", "602"); + qDebug() << "halconStart" << QString::fromStdString(data->SNcode); + procedureCall.Execute(); + qDebug() << "halconEnd" << QString::fromStdString(data->SNcode); + // *output(HTuple) : < / c> + // *out_Circle, 圆心坐标(y, x, radius) < / c > + // * out_Flag, 算法执行成功标志位, 0 : 算法执行失败 1 : 算法执行成功< / c> + // *out_OffsetX : 模组X轴的补偿量(mm) < / c > + // *out_OffsetY : 模组Y轴的补偿量(mm) < / c > + HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); + HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); + HTuple out_OffsetX = procedureCall.GetOutputCtrlParamTuple("out_OffsetX"); + HTuple out_OffsetY = procedureCall.GetOutputCtrlParamTuple("out_OffsetY"); + HTuple out_NgTails = procedureCall.GetOutputCtrlParamTuple("out_NgTails"); + +#pragma endregion + int outFlag = out_Flag.D(); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera::放钉" << "_SN::" << SN << ",放钉算法结果;" << out_Flag[0].D(); + cvPainter textPainter; + + if (outFlag == 1) { //-1为失败,无结果 + //先处理数据 + double offsetX = 0 - out_OffsetX.D(); + double offsetY = 0 - out_OffsetY.D(); + /* if (address.triggerAddr == 20200 || address.triggerAddr == 20202 || address.triggerAddr == 20600 || address.triggerAddr == 20602) { + offsetX = -offsetX; + }*/ + //if (address.triggerAddr == 20000) { + // offsetY += 0.15; + //} + //else if (address.triggerAddr == 20002) { + // offsetY += 0.2; + //} + //else if (address.triggerAddr == 20200) { + // offsetX -= 0.05; + // offsetY -= 0.15; + //} + //else if (address.triggerAddr == 20202) { + // offsetX -= 0.1; + //} + //else if (address.triggerAddr == 20400) { + // //offsetX -= 0.1; + //} + //else if (address.triggerAddr == 20402) { + // //offsetX -= 1.2; + //} + if (abs(offsetX) < 0.01) + { + offsetX = 0.01; + } + if (abs(offsetY) < 0.01) + { + offsetY = 0.01; + } + offsetX *= 1000;//和激光交流后要求我们*1000 + offsetY *= 1000; + double X_Circle = out_Circle[1].D(); + double Y_Circle = out_Circle[0].D(); + + if (abs(offsetX) >= 6000 || abs(offsetY) >= 6000) { + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123,0.0123");//发送偏移X Y + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 + qDebug() << __FUNCTION__ << " line:" << __LINE__ << " 满焊X偏移abs(resultData[0]) >= 6000,may err,set 0"; + data->halconOK = false; + } + else { + sqlMapData["偏移X"] = offsetX; + sqlMapData["偏移Y"] = offsetY; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(offsetX) + "," + QByteArray::number(offsetY));//发送偏移X Y + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + data->halconOK = true; + } + //圆及十字 + textPainter.appendPaintJob(cv::Point(out_Circle[1].D(), out_Circle[0].D()), out_Circle[2].D(), cv::Scalar(0, 255, 0), 5, 0); + textPainter.appendPaintJob(cv::Point(out_Circle[1].D() - 300, out_Circle[0].D()), cv::Point(out_Circle[1].D() + 300, out_Circle[0].D()), cv::Scalar(0, 255, 0), 5); + textPainter.appendPaintJob(cv::Point(out_Circle[1].D(), out_Circle[0].D() - 300), cv::Point(out_Circle[1].D(), out_Circle[0].D() + 300), cv::Scalar(0, 255, 0), 5); + + textPainter.appendPaintJob("X偏移:" + std::to_string(offsetX), cv::Point(50, 150), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("Y偏移:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); + } + else { + data->halconOK = false; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123,0.0123");//发送偏移X + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 + std::string ngDetail = out_NgTails.ToString().Text(); + textPainter.appendPaintJob(ngDetail, cv::Point(50, 300), cv::Scalar(0, 0, 255)); + } + + if (data->halconOK == true) { + textPainter.appendPaintJob("OK", cv::Point(50, 550), cv::Scalar(0, 255, 0)); + sqlMapData["结果"] = "OK"; + } + else { + textPainter.appendPaintJob("NG", cv::Point(50, 500), cv::Scalar(0, 0, 255)); + sqlMapData["结果"] = "NG"; + } + textPainter.appendPaintJob("相机:" + cameraNumberStr + " 孔" + FixtureNumber, cv::Point(50, 0), cv::Scalar(0, 255, 0)); + data->service->_dataRecorder->addData(QString::fromStdString(data->SNcode), sqlMapData); + + //int rate = MIN(Paint.cols, Paint.rows) / 20; + //TextRendererZH textPainter2(Paint.cols, Paint.rows, rate); + //textPainter2.putText(Paint, "获取图像时间:" + currentimenow.toString().toStdString(), cv::Point(50, Paint.rows-rate), cv::Scalar(0, 255, 255)); + //textPainter2.putText(Paint, "SN条码:" + data->SNcode, cv::Point(50, Paint.rows - (rate*2), cv::Scalar(0, 255, 255)); + textPainter.executePaintJobs(Paint); + putSnAndDetTime(Paint, data->SNcode, 100); + data->insertResultImg(Paint); + + return true; + } + CATCH_EXCEPTIONS; + qDebug() << "error3" << QString::fromStdString(data->SNcode); + exceptionResume(data, address, "异常"); + return false; +} +static bool YuHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address) { + try { + std::chrono::steady_clock::time_point algoStartTime = std::chrono::steady_clock::now(); + std::map sqlMapData; + sqlMapData["项目"] = "预焊"; + sqlMapData["相机"] = "相机" + address.triggerAddr; + ///////////////////取图//处理////////////////// + if (data->getOriImgSize() < 1) { + exceptionResume(data, address, "图片数量不满足要求"); + qDebug() << "图片数量不满足要求:" << data->getOriImgSize(); + return false; + } + + /*QDateTime currentimenow = QDateTime::currentDateTime();*/ + cv::Mat Paint; + cv::Mat src1 = data->getOriImg(0); + //src1 = cv::imread("D:\\04QCB82602000JFCA00600BN_Camera1_0_04-18-02.bmp",-1); + //src1 = cv::imread("C:\\Users\\Main\\MVS\\Data\\Image_20251205093757260.bmp", -1); + if (src1.channels() != 3) + cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); + else + src1.copyTo(Paint); + //处理算法 +#pragma region halcon处理 + std::string cameraNumberStr = "1", FixtureNumber = "1"; + if (address.triggerAddr == 19600) { + cameraNumberStr = "1"; + FixtureNumber = "1"; + } + else if (address.triggerAddr == 19602) { + cameraNumberStr = "1"; + FixtureNumber = "3"; + } + else if (address.triggerAddr == 19800) { + cameraNumberStr = "2"; + FixtureNumber = "2"; + } + else if (address.triggerAddr == 19802) { + cameraNumberStr = "2"; + FixtureNumber = "4"; + } + data->customPath = "{imgSaveRootDir}/{yyyy-MM-dd}/{saveObjectName}{imageType}/{HH}H/{processResult}/相机" + QString::fromStdString(cameraNumberStr) + "孔" + QString::fromStdString(FixtureNumber);; + int pingDing_aoDing_Switch = data->service->_paramSyncManager->get("预焊", "1平钉2凹钉", 2).toInt(); + int YuHanbiaoding_Switch = data->service->_paramSyncManager->get("预焊", "模式选择:1标定0生产", 0).toInt(); + if (YuHanbiaoding_Switch == 1) + data->service->_paramSyncManager->edit("预焊", "1标定0生产", 0); + double tongXingJuThreshold = data->service->_paramSyncManager->get("预焊", "同心距上限", 0.1).toDouble(); + double pianyizhiThreshold = data->service->_paramSyncManager->get("预焊", "偏移值上限", 0.1).toDouble(); + HDevEngineCpp::HDevProcedure proc("_9_yuhan"); + HalconCpp::HObject in_Image = MatToHObject(src1); + HDevProcedureCall procedureCall(proc); + procedureCall.SetInputIconicParamObject("in_Image", in_Image); + procedureCall.SetInputCtrlParamTuple("in_ProductionType", YuHanbiaoding_Switch); + procedureCall.SetInputCtrlParamTuple("in_Resolution", HTuple(0.0069699)); + procedureCall.SetInputCtrlParamTuple("in_CameraNumber", HTuple(cameraNumberStr.c_str())); + procedureCall.SetInputCtrlParamTuple("in_FixtureNumber", HTuple(FixtureNumber.c_str())); + procedureCall.SetInputCtrlParamTuple("in_FilePath1", HTuple(("./halconFiles/template_yuhan/" + data->cameraKeyName + "/").c_str())); + procedureCall.SetInputCtrlParamTuple("in_FilePath2", HTuple("./halconFiles/base_yuhan/")); + procedureCall.SetInputCtrlParamTuple("in_NailType", pingDing_aoDing_Switch);//1:平钉 2:凹钉; + //procedureCall.SetInputCtrlParamTuple("in_LineType", "602"); + qDebug() << "halconStart" << QString::fromStdString(data->SNcode); + procedureCall.Execute(); + qDebug() << "halconEnd" << QString::fromStdString(data->SNcode); + + // *output(HTuple) : < / c> + // *out_Circle, 圆心坐标(y, x, radius) < / c > + // * out_Flag, 算法执行成功标志位, 0 : 算法执行失败 1 : 算法执行成功< / c> + // *out_OffsetX : 模组X轴的补偿量(mm) < / c > + // *out_OffsetY : 模组Y轴的补偿量(mm) < / c > + HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); + HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); + HTuple out_OffsetX = procedureCall.GetOutputCtrlParamTuple("out_OffsetX"); + HTuple out_OffsetY = procedureCall.GetOutputCtrlParamTuple("out_OffsetY"); + HTuple out_NgTails = procedureCall.GetOutputCtrlParamTuple("out_NgTails"); + HTuple out_CCDistance = procedureCall.GetOutputCtrlParamTuple("out_CCDistance"); + HTuple out_OffsetValue = procedureCall.GetOutputCtrlParamTuple("out_OffsetValue"); +#pragma endregion + int outFlag = out_Flag.D(); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera::放钉" << "_SN::" << SN << ",放钉算法结果;" << out_Flag[0].D(); + cvPainter textPainter; + textPainter.appendPaintJob("相机:" + cameraNumberStr + " 孔位" + FixtureNumber + (pingDing_aoDing_Switch == 1 ? " 平钉算法" : " 凹钉算法"), cv::Point(50, 0), cv::Scalar(0, 255, 0)); + if (outFlag == 1) { //-1为失败,无结果 + double offsetX = out_OffsetX.D(); + double offsetY = out_OffsetY.D(); + double offsetNum = out_OffsetValue.D(); + double CCDdist = out_CCDistance.D(); + //if (address.triggerAddr == 19600) + // offsetX -= 0.2; + //else if (address.triggerAddr == 19602) { + // offsetX -= 0.15; + // offsetY += 0.1; + //} + //else (address.triggerAddr == 19800) { + // offsetY -= 0.2; + //} + + //圆及十字 + textPainter.appendPaintJob(cv::Point(out_Circle[1].D(), out_Circle[0].D()), out_Circle[2].D(), cv::Scalar(0, 255, 0), 5, 0); + textPainter.appendPaintJob(cv::Point(out_Circle[1].D() - 300, out_Circle[0].D()), cv::Point(out_Circle[1].D() + 300, out_Circle[0].D()), cv::Scalar(0, 255, 0), 5); + textPainter.appendPaintJob(cv::Point(out_Circle[1].D(), out_Circle[0].D() - 300), cv::Point(out_Circle[1].D(), out_Circle[0].D() + 300), cv::Scalar(0, 255, 0), 5); + + textPainter.appendPaintJob("X偏移:" + std::to_string(offsetX), cv::Point(50, 150), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("Y偏移:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("同心距:" + std::to_string(CCDdist), cv::Point(50, 550), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("偏移值:" + std::to_string(offsetNum), cv::Point(50, 750), cv::Scalar(0, 255, 0)); + + if (std::abs(tongXingJuThreshold) < CCDdist) { + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123,0.0123," + QByteArray::number(offsetNum));//发送偏移X Y 同心距 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "同心距NG"; + textPainter.appendPaintJob("同心距NG", cv::Point(250, 950), cv::Scalar(0, 0, 255)); + data->halconOK = false; + } + else if (std::abs(pianyizhiThreshold) < offsetNum) { + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123,0.0123," + QByteArray::number(offsetNum));//发送偏移X Y 同心距 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "偏移值NG"; + textPainter.appendPaintJob("偏移值NG", cv::Point(250, 950), cv::Scalar(0, 0, 255)); + data->halconOK = false; + } + else if (abs(offsetX) >= 6000 || abs(offsetY) >= 6000) { + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123,0.0123," + QByteArray::number(offsetNum));//发送偏移X Y 同心距 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 + qDebug() << __FUNCTION__ << " line:" << __LINE__ << " 预焊X偏移abs(resultData[0]) >= 6000,may err,set 0"; + data->halconOK = false; + } + else { + sqlMapData["偏移X"] = offsetX; + sqlMapData["偏移Y"] = offsetY; + if (offsetX || offsetY == 0) + { + offsetX += 0.01; + offsetY += 0.01; + } + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(offsetX) + "," + QByteArray::number(offsetY) + "," + QByteArray::number(offsetNum));//发送偏移X + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + data->halconOK = true; + } + } + else { + data->halconOK = false; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123,0.0123");//发送偏移X + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 + std::string ngDetail = out_NgTails.ToString().Text(); + textPainter.appendPaintJob(ngDetail, cv::Point(50, 300), cv::Scalar(0, 0, 255)); + } + + if (data->halconOK == true) { + textPainter.appendPaintJob("OK", cv::Point(50, 950), cv::Scalar(0, 255, 0)); + sqlMapData["结果"] = "OK"; + } + else { + textPainter.appendPaintJob("NG", cv::Point(50, 950), cv::Scalar(0, 0, 255)); + sqlMapData["结果"] = "NG"; + } + data->service->_dataRecorder->addData(QString::fromStdString(data->SNcode), sqlMapData); + std::chrono::steady_clock::time_point algoEndTime = std::chrono::steady_clock::now(); + qDebug() << "预焊算法耗时:" << std::chrono::duration_cast(algoEndTime - algoStartTime).count(); + textPainter.executePaintJobs(Paint); + putSnAndDetTime(Paint, data->SNcode, 100); + data->insertResultImg(Paint); + + return true; + } + CATCH_EXCEPTIONS; + qDebug() << "error" << QString::fromStdString(data->SNcode); + exceptionResume(data, address, "异常"); + return false; +} + +bool aiEvaluate(std::shared_ptr data, cv::Mat& input, cv::Mat& Paint, int index, mfd_hh_models* models, cv::Size fillter) { + try { + bool isNg = false; + HDevEngineCpp::HDevProcedure proc("_9_Crop_Inspection_Image_HJ"); + HalconCpp::HObject in_Image_2D = MatToHObject(input); + HDevProcedureCall procedureCall(proc); + procedureCall.SetInputIconicParamObject("in_Image", in_Image_2D); + procedureCall.SetInputCtrlParamTuple("in_CropType", 0); + procedureCall.SetInputCtrlParamTuple("in_FilePath", "./halconFiles/mfd_hh_cut/"); + procedureCall.Execute(); + HObject out_InspectionImage = procedureCall.GetOutputIconicParamObject("out_InspectionImage"); + HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); + HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); + if (out_Flag.D() == 1) { + std::vector> clsImgs; + cv::Mat forSeg = HObject2Mat(out_InspectionImage); + //forSeg(cv::Rect(center.x - 650, center.y - 650, 1300, 1300)).copyTo(forSeg); + cv::resize(forSeg, forSeg, cv::Size(), 0.5, 0.5); + data->insertSegOriginImg(forSeg); + cv::Mat segResult = ~models->seg_hanHou->Evaluate(forSeg); + std::vector>contours;//点集 + cv::findContours(segResult, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); // 寻找白色区域并储存为点集 + + TextRendererZH textPainter(Paint.cols, Paint.rows, 20); + for (int i = 0; i < contours.size(); i++) {//遍历点集 + cv::Rect rect = cv::boundingRect(contours[i]);//最小外接矩形 + + if (rect.height > fillter.height || rect.width > fillter.width) {// 过滤 只有都大于10像素才进检测 + cv::Mat clsImg = cutImg2Cls(rect, forSeg); + int clsResult = models->cls_hanHou->Evaluate(clsImg); + double prob = models->cls_hanHou->getProbability(); + clsImgs.push_back({ "Result" + std::to_string(clsResult) + "_prob" + std::to_string(prob),clsImg }); + if (clsResult != 0 && prob > 0.6) { + cv::drawContours(Paint, contours, i, cv::Scalar(0, 0, 255), 3, 8, cv::noArray(), INT_MAX); + std::string text; + switch (clsResult) + { + case 1: + text = "焊渣"; + break; + case 2: + text = "裂纹"; + break; + case 3: + text = "炸焊"; + break; + case 4: + text = "焊烟"; + break; + } + //textPainter.putText(Paint, text, cv::Point(0,0), cv::Scalar(0, 0, 255)); + isNg = true; + } + } + } + data->insertClsOriginImg(clsImgs); + } + else { + isNg = true; + } + return isNg; + } + CATCH_EXCEPTIONS; + return false; + +} +static bool HanHou2D(std::shared_ptr data, mfdHanHouParam* _detParams, yh_581_address address) { + cv::Mat segResult; + cv::Mat heightImg; + data->SNcode += "_" + std::to_string(address.index + 1); + try { + if (data->getOriImgSize() < 1) { + qCritical() << "图片数量不满足要求:" << data->getOriImgSize() << QString::fromStdString(data->SNcode) << QString::fromStdString(data->cameraKeyName); + exceptionResumeHH(data, address, "图片数量不满足要求"); + return false; + } + if (data->getOriImg(0).rows == 200) { + qCritical() << "取图超时:" << data->getOriImgSize() << QString::fromStdString(data->SNcode) << QString::fromStdString(data->cameraKeyName); + exceptionResumeHH(data, address, "取图超时"); + return false; + } + cv::Mat Paint; + cv::Mat src1 = data->getOriImg(0); + //src1 = cv::imread("D:\\test\\04QCB82602000JFCD00776BN_Camera7_0_04-56-43.bmp", -1);//OK + //src1 = cv::imread("D:\\test\\ng\\04QCB82602000JG1A01995BN_3_Camera7_ORI_0_11-04-03-635.bmp", -1);//NG + //处理算法 +#pragma region halcon处理 + cv::Mat forHalcon; + cv::resize(src1, forHalcon, cv::Size(), 0.5, 0.5); + if (forHalcon.channels() != 3) + cv::cvtColor(forHalcon, Paint, cv::COLOR_GRAY2BGR); + else + forHalcon.copyTo(Paint); + segResult = _detParams->models[address.index].seg_hanHouDingWei->Evaluate(forHalcon); + data->insertSegOriginImg(segResult); + data->insertSegOriginImg(forHalcon); + bool waitResult = _detParams->waitImage(heightImg, address.index); + if (!waitResult) { + qCritical() << "3D等图超时:" << data->getOriImgSize() << QString::fromStdString(data->SNcode) << QString::fromStdString(data->cameraKeyName); + exceptionResumeHH(data, address, "3D等图超时"); + return false; + } + double MFDQD = data->service->_paramSyncManager->get("焊后", "密封钉翘钉设定值(mm)", 0.15).toDouble(); + //forHalcon = cv::imread("D:/测试/算法测试/04QCB82602000JG1N050681P_2_Camera7_ORI_0_20-09-27-650.bmp", -1); + //heightImg = cv::imread("D:/测试/算法测试/04QCB82602000JG1N050681P_2_Camera8_ORI_1_20-09-26-883.tiff", -1); + //segResult = cv::imread("D:/测试/算法测试/04QCB82602000JG1N050681P_2_Camera7_SEG_0_20-09-27-666.bmp", -1); + + HDevEngineCpp::HDevProcedure proc("_9_Cal_2D_3D_PostWelding"); + HDevProcedureCall procedureCall(proc); + + //procedureCall.SetWaitForDebugConnection(true); + + HalconCpp::HObject in_Image_2D = MatToHObject(src1); + HalconCpp::HObject in_Image3DH = MatToHObject(heightImg); + //cv::imwrite("D://testCache//" + data->SNcode + ".tiff", heightImg); + HalconCpp::HObject in_Image_seg = MatToHObject(segResult); + + procedureCall.SetInputIconicParamObject("in_Image_2D", in_Image_2D); + procedureCall.SetInputIconicParamObject("in_Image3DH", in_Image3DH); + procedureCall.SetInputIconicParamObject("in_Image_seg", in_Image_seg); + + procedureCall.SetInputCtrlParamTuple("in_xResolution", HTuple(0.005)); + // procedureCall.SetInputCtrlParamTuple("in_yResolution", HTuple(0.0438071)); + //procedureCall.SetInputCtrlParamTuple("in_yResolution", HTuple(0.11)); + procedureCall.SetInputCtrlParamTuple("in_yResolution", HTuple(0.15)); + procedureCall.SetInputCtrlParamTuple("in_zResolution", HTuple(0.0008)); + procedureCall.SetInputCtrlParamTuple("in_FilePath", ("./halconFiles/mfd_hh/" + std::to_string(address.index + 1) + "/").c_str()); + procedureCall.SetInputCtrlParamTuple("in_2D_pix", HTuple(0.00685845)); + procedureCall.SetInputCtrlParamTuple("in_Fixture_Number", address.index + 1); + HTuple OffsetValue; + for (int i = 0; i < 8; i++) + OffsetValue.Append(0); + procedureCall.SetInputCtrlParamTuple("in_offsetValue", OffsetValue); + procedureCall.Execute(); + HTuple out_CC_Distance = procedureCall.GetOutputCtrlParamTuple("out_CC_Distance"); + HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); + HTuple out_DefectHeight = procedureCall.GetOutputCtrlParamTuple("out_MFDHeight"); + HTuple Out_Angle = procedureCall.GetOutputCtrlParamTuple("out_Angle"); + HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); + HTuple out_NgDetails = procedureCall.GetOutputCtrlParamTuple("out_NgDetails"); +#pragma endregion + + int outFlag = out_Flag.D(); + QByteArray plcResult = "1"; + cvPainter textPainter; + if (outFlag == 1) { //-1为失败,无结果 + cv::Mat forHanddleAi; + double CC_Distance = out_CC_Distance.D(); + for (int i = 0; i < out_Circle.Length() / 3; i++) { + cv::Point2f center1(out_Circle[i * 3 + 1].D(), out_Circle[i * 3].D()); + double radius1 = out_Circle[i * 3 + 2].D(); + cv::circle(Paint, center1 / 2, radius1 / 2, cv::Scalar(0, 255, 0), 3); + } + + double defectHeight = out_DefectHeight.D();//密封钉高度 + double angle = Out_Angle.D();//密封钉角度 + + textPainter.appendPaintJob("密封钉高度:" + std::to_string(defectHeight), cv::Point(50, 120), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("密封钉平面度:" + std::to_string(angle), cv::Point(50, 180), cv::Scalar(0, 255, 0)); + textPainter.appendPaintJob("密封钉同心距:" + std::to_string(CC_Distance), cv::Point(50, 240), cv::Scalar(0, 255, 0)); + + bool result = aiEvaluate(data, src1, Paint, address.index, &_detParams->models[address.index], + cv::Size(data->service->_paramSyncManager->get("焊后", "过滤宽(pix)", 50).toDouble(), data->service->_paramSyncManager->get("焊后", "过滤高(pix)", 50).toDouble())); + + if (result == true) { + data->aiDetOK = false; + textPainter.appendPaintJob("NG", cv::Point(50, 10), cv::Scalar(0, 0, 255)); + } + + if (result == true && data->service->_paramSyncManager->get("焊后", "模型判定启用", false).toBool()) + plcResult = "2"; + + //cv::circle(Paint, center2 / 2, radius2 / 2, cv::Scalar(0, 255, 0), 3); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(CC_Distance) + "," + QByteArray::number(defectHeight) + "," + QByteArray::number(angle));//发送同心距 + //data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(defectHeight));//发送密封钉高度 + //data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 4) + "_" + QByteArray::number(angle));//发送密封钉角度 + + if (std::abs(MFDQD) < Out_Angle.D()) { + data->halconOK = false; + //plcResult = "2"; + textPainter.appendPaintJob("NG", cv::Point(50, 10), cv::Scalar(0, 0, 255)); + textPainter.appendPaintJob("翘钉NG", cv::Point(250, 10), cv::Scalar(0, 0, 255)); + } + } + else { + data->halconOK = false; + //plcResult = "2"; + textPainter.appendPaintJob("NG", cv::Point(50, 10), cv::Scalar(0, 0, 255)); + std::string ngText = out_NgDetails.ToString().Text(); + textPainter.appendPaintJob(ngText, cv::Point(250, 10), cv::Scalar(0, 0, 255)); + data->insertSegOriginImg(heightImg); + } + + if (plcResult == "2" || data->aiDetOK == false || data->halconOK == false) { + textPainter.appendPaintJob("NG", cv::Point(50, 10), cv::Scalar(0, 0, 255)); + } + else { + textPainter.appendPaintJob("OK", cv::Point(50, 10), cv::Scalar(0, 255, 0)); + } + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + textPainter.executePaintJobs(Paint); + putSnAndDetTime(Paint, data->SNcode, 100); + data->insertResultImg(Paint); + _detParams->insertResult(data->SNcode, data->halconOK); + return true; + } + CATCH_EXCEPTIONS; + exceptionResumeHH(data, address, "异常"); +} +static bool HanHou3D(std::shared_ptr data, mfdHanHouParam* _detParams, yh_581_address address) { + try { + if (address.triggerAddr == 21000) + _detParams->clear(); + data->SNcode += "_" + std::to_string(address.index + 1); + + if (data->getOriImgSize() < 2) { + qCritical() << "图片数量不满足要求:" << data->getOriImgSize() << QString::fromStdString(data->SNcode) << QString::fromStdString(data->cameraKeyName); + exceptionResumeHH(data, address, "图片数量不满足要求"); + return false; + } + if (data->getOriImg(0).rows == 1000 && data->getOriImg(0).cols == 1000) { + qCritical() << "取图超时:" << data->getOriImgSize() << QString::fromStdString(data->SNcode) << QString::fromStdString(data->cameraKeyName); + exceptionResumeHH(data, address, "取图超时"); + return false; + } + ///////////////////取图//处理////////////////// + //if (data->getOriImgSize() < 1) { + // qCritical() << "图片数量不满足要求:" << data->getOriImgSize(); + // return false; + //} + cv::Mat Paint; + cv::Mat src1 = data->getOriImg(0); + cv::Mat heightImg = data->getOriImg(1); + //heightImg = cv::imread("D:\\test\\04QCB82602000JFCD00776BN_Camera8_1_04-56-43.tiff", -1);//OK + //heightImg = cv::imread("D:\\test\\ng\\04QCB82602000JG1A01995BN_3_Camera8_ORI_1_11-04-03-222.tiff", -1); + if (heightImg.channels() != 3) + cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); + else + src1.copyTo(Paint); + _detParams->insertImg(heightImg, address.index); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + TextRendererZH textPainter(Paint.cols, Paint.rows, 50); + data->halconOK = _detParams->waitResult(data->SNcode); + if (data->halconOK == true) { + textPainter.putText(Paint, "OK", cv::Point(50, 10), cv::Scalar(0, 255, 0)); + } + else { + textPainter.putText(Paint, "NG", cv::Point(50, 10), cv::Scalar(0, 0, 255)); + } + putSnAndDetTime(Paint, data->SNcode, 50); + data->insertResultImg(Paint); + return true; + } + CATCH_EXCEPTIONS; + exceptionResumeHH(data, address, "异常"); +} +#pragma endregion +class _581_Mfd : public mission { +public: + _581_Single_triggerTemplate< mfd_detParams> yh_1; + _581_Single_triggerTemplate< mfd_detParams> yh_2; + + _581_Single_triggerTemplate< mfd_detParams> mh_1; + _581_Single_triggerTemplate< mfd_detParams> mh_2; + _581_Single_triggerTemplate< mfd_detParams> mh_3; + _581_Single_triggerTemplate< mfd_detParams> mh_4; + + _581_Union_triggerTemplate< mfdHanHouParam> hh_2D; + _581_Union_triggerTemplate< mfdHanHouParam> hh_3D; + + mfd_detParams _params; + + mfdHanHouParam _hhParams; + void onConfigReloaded() { + HDevEngineCpp::HDevEngine myEngine; + myEngine.SetProcedurePath(" "); + myEngine.SetProcedurePath("./halconAlgo/AlgoLibrary_602.hdpl"); + + //myEngine.StartDebugServer(); + + for (auto& model : _hhParams.models) { + model.seg_hanHou = handdleAI::CreateSegObjS("./handleaiModels/seg_hanhou.BHD", 16, 0); + model.cls_hanHou = handdleAI::CreateClsObjV3("./handleaiModels/cls_hanhou.BHD", 0, 1); + model.seg_hanHouDingWei = handdleAI::CreateSegObjS("./handleaiModels/seg_hanhouDingWei.BHD", 16, 0); + } + } + bool initCustom() { +#pragma region 注册触发器回调 + yh_1.init("Camera1", "预焊1", 19600, 2, 1, &_params, _serviceManager, bridge, YuHan, "预焊CCD"); + yh_2.init("Camera2", "预焊2", 19800, 2, 1, &_params, _serviceManager, bridge, YuHan, "预焊CCD"); + + mh_1.init("Camera3", "满焊1", 20000, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); + mh_2.init("Camera4", "满焊2", 20200, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); + mh_3.init("Camera5", "满焊3", 20400, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); + mh_4.init("Camera6", "满焊4", 20600, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); + + hh_2D.init("Camera7", "焊后2d", 20800, 4, 1, &_hhParams, _serviceManager, bridge, HanHou2D, "焊后2D&3D"); + hh_3D.init("Camera8", "焊后3d", 21000, 4, 1, &_hhParams, _serviceManager, bridge, HanHou3D, "焊后2D&3D"); +#pragma endregion + return true; + } + + //9600 + +}; +#pragma once \ No newline at end of file diff --git a/src/mission/old/581-Mfd_yh_mh_hh/CMakeLists.txt b/src/mission/old/581-Mfd_yh_mh_hh/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/old/581-Mfd_yh_mh_hh/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.cpp b/src/mission/old/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.cpp similarity index 97% rename from src/mission/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.cpp rename to src/mission/old/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.cpp index 9cfa188..65b8657 100644 --- a/src/mission/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.cpp +++ b/src/mission/old/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.cpp @@ -40,7 +40,7 @@ void exceptionResume(std::shared_ptr data, dianJiaoParams* _detParams, //else // data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 } -QByteArray halconAsyncFuncGlueArea(TextRendererZH* render,std::shared_ptr data,cv::Mat& Paint, cv::Mat& sourceImg, double Resolution, std::string ProductType,double areaOffset = 0) { +QByteArray halconAsyncFuncGlueArea(cvPainter* render,std::shared_ptr data,cv::Mat& Paint, cv::Mat& sourceImg, double Resolution, std::string ProductType,double areaOffset = 0) { double areaNgRate = data->service->_paramSyncManager->get("点胶设置","胶体比例最小阈值(%)", 90.0).toDouble(); double areaNgEnable = data->service->_paramSyncManager->get("点胶设置","胶体比例判定启用", true).toBool(); HDevEngineCpp::HDevProcedure proc("_3_Inspection_Glue"); @@ -165,7 +165,7 @@ bool DianJiaoJianCe(std::shared_ptr data, dianJiaoParams* _detParams, double Resolution = 0.014901; - TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + cvPainter textPainter; double offsetArea = 0; /* if (address.triggerAddr == 110 || address.triggerAddr == 210) offsetArea = 0.1 ;*/ @@ -255,8 +255,8 @@ bool TieJiaoJianCe(std::shared_ptr data, tieJiaoParams* _detParams, sx if (Paint.channels() == 1) cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - TextRendererZH textPainter(Paint.cols, Paint.rows, 800); - TextRendererZH textPainter2(Paint.cols, Paint.rows, 400); + cvPainter textPainter; + cvPainter textPainter2; if ((address.triggerAddr == 310 && (address.triggerValue == 1 || address.triggerValue == 3))|| (address.triggerAddr == 810 && (address.triggerValue == 1 || address.triggerValue == 3))) { _detParams->productType = "CU"; diff --git a/src/mission/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.h b/src/mission/old/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.h similarity index 100% rename from src/mission/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.h rename to src/mission/old/581_Ruanlianjie_dianjiao_tiejiao/581_Ruanlianjie_dianjiao_tiejiao.h diff --git a/src/mission/old/581_Ruanlianjie_dianjiao_tiejiao/CMakeLists.txt b/src/mission/old/581_Ruanlianjie_dianjiao_tiejiao/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/old/581_Ruanlianjie_dianjiao_tiejiao/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.cpp b/src/mission/old/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.cpp similarity index 97% rename from src/mission/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.cpp rename to src/mission/old/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.cpp index ee37484..7abeecb 100644 --- a/src/mission/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.cpp +++ b/src/mission/old/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.cpp @@ -91,9 +91,9 @@ bool HanQianDingWei(std::shared_ptr data, hanQianDingWeiParams* _detPa //正确的应该左边铝 右边铜 double value1_1 = 0, value1_2 = 0, value2_1 = 0, value2_2 = 0; bool jiXingEnable = data->service->_paramSyncManager->get("焊前定位", "焊前极性判定启用", false).toBool(); - TextRendererZH textPainter(Paint.cols, Paint.rows, 200); - TextRendererZH textPainter2(Paint.cols, Paint.rows, 600); - TextRendererZH textPainter3(Paint.cols, Paint.rows, 800); + cvPainter textPainter; + cvPainter textPainter2; + cvPainter textPainter3; if (flag == 1) { //circle(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D()), out_Circle[2].D(), cv::Scalar(0, 255, 0), 5); @@ -214,7 +214,7 @@ struct halconResult_2D { double waiYuanPianYi_X = -99, waiYuanPianYi_Y = -99, waiYuanTongXinDu = -99; bool halconOK = false; }; -halconResult_2D HanHou2D_halconEvaluate(const cv::Mat& sourceImg,const cv::Mat& segImg, TextRendererZH* textPainter, int times, +halconResult_2D HanHou2D_halconEvaluate(const cv::Mat& sourceImg,const cv::Mat& segImg, cvPainter* textPainter, int times, double tongXingDuLimitHigh, double tongXingDuLimitLow, bool tongXingDuLimitEnable, double neiYuanPianYiTongXinDuLimitHigh, double neiYuanPianYiTongXinDuLimitLow, bool neiYuanPianYiTongXinDuEnable, double waiYuanPianYiTongXinDuLimitHigh, double waiYuanPianYiTongXinDuLimitLow, bool waiYuanPianYiTongXinDuEnable) { @@ -304,7 +304,7 @@ halconResult_2D HanHou2D_halconEvaluate(const cv::Mat& sourceImg,const cv::Mat& } return result; } -bool HanHou2D_handleAiEvaluate(std::shared_ptr data, hanHouJianCe* _detParams,const cv::Mat& sourceImg, TextRendererZH* textPainter,handdleAI::pSegmentation* seg,handdleAI::pClassification* cls) { +bool HanHou2D_handleAiEvaluate(std::shared_ptr data, hanHouJianCe* _detParams,const cv::Mat& sourceImg, cvPainter* textPainter,handdleAI::pSegmentation* seg,handdleAI::pClassification* cls) { double hanZhaFillterWidth = data->service->_paramSyncManager->get("焊后", "焊渣过滤值宽mm(上限)", 0.1).toDouble()/ 0.013736; double hanZhaFillterHeight = data->service->_paramSyncManager->get("焊后", "焊渣过滤值高mm(上限)", 0.1).toDouble()/ 0.013736; bool hanZhaEnable = data->service->_paramSyncManager->get("焊后", "焊渣判定启用", false).toBool(); @@ -427,7 +427,7 @@ bool HanHouJianCe2D(std::shared_ptr data, hanHouJianCe* _detParams, sx if (lvRect.width < 100 || lvRect.height < 100) { isNg2 = true; } - TextRendererZH textPainter1(Paint1.cols, Paint1.rows, 200), textPainter2(Paint2.cols, Paint2.rows, 200); + cvPainter textPainter1, textPainter2; //判斷焊印結果 if (isNg1 == true || isNg2 == true) {//判断存图 data->halconOK = false; @@ -561,7 +561,7 @@ struct halconResult_3D { bool halconOK = false; }; halconResult_3D HanHou3D_halconEvaluate(const cv::Mat& heightImg, const double& xResolution, const double& yResolution, const double& zResolution - , TextRendererZH* textPainter) { + , cvPainter* textPainter) { halconResult_3D result; HDevEngineCpp::HDevProcedure proc("_3_Inspection_HY_Defect"); @@ -632,7 +632,7 @@ bool HanHouJianCe3D(std::shared_ptr data, hanHouJianCe* _detParams, sx cv::Mat height1 = data->getOriImg(1);//高度图1 cv::Mat src2 = data->getOriImg(2);//亮度图2 cv::Mat height2 = data->getOriImg(3);//高度图2 - TextRendererZH textPainter1(src1.cols, src1.rows, 100), textPainter2(src2.cols, src2.rows, 100); + cvPainter textPainter1, textPainter2; auto result1 = HanHou3D_halconEvaluate(height1, data->service->_paramSyncManager->get("3D相机1精度","x向精度",0.005).toDouble(), data->service->_paramSyncManager->get("3D相机1精度", "y向精度", 0.02).toDouble(), @@ -656,9 +656,9 @@ bool HanHouJianCe3D(std::shared_ptr data, hanHouJianCe* _detParams, sx if (result2.ellipseCenter.x != 0) cv::ellipse(Paint2, result2.ellipseCenter, result2.ellipseSize, result2.angle, 0, 360, cv::Scalar(0, 255, 0), 3); - textPainter1.putText(Paint1, "OK", cv::Point(10, 10), cv::Scalar(0, 255, 0)); + //textPainter1.putText(Paint1, "OK", cv::Point(10, 10), cv::Scalar(0, 255, 0)); textPainter1.executePaintJobs(Paint1); - textPainter2.putText(Paint2, "OK", cv::Point(10, 10), cv::Scalar(0, 255, 0)); + //textPainter2.putText(Paint2, "OK", cv::Point(10, 10), cv::Scalar(0, 255, 0)); textPainter2.executePaintJobs(Paint2); cv::vconcat(Paint1, Paint2, PaintSum); putSnAndDetTime(PaintSum, data->SNcode, 60); diff --git a/src/mission/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.h b/src/mission/old/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.h similarity index 100% rename from src/mission/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.h rename to src/mission/old/581_Ruanlianjie_hanqian_hanhou/581_Ruanlianjie_hanqian_hanhou.h diff --git a/src/mission/old/581_Ruanlianjie_hanqian_hanhou/CMakeLists.txt b/src/mission/old/581_Ruanlianjie_hanqian_hanhou/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/old/581_Ruanlianjie_hanqian_hanhou/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) + +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ${PROJECT_ROOT_PATH}/include/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.cpp b/src/mission/old/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.cpp similarity index 100% rename from src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.cpp rename to src/mission/old/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.cpp diff --git a/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.h b/src/mission/old/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.h similarity index 100% rename from src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.h rename to src/mission/old/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.h diff --git a/src/mission/581_YiCiZhuYe_canye/CMakeLists.txt b/src/mission/old/581_YiCiZhuYe_canye/CMakeLists.txt similarity index 100% rename from src/mission/581_YiCiZhuYe_canye/CMakeLists.txt rename to src/mission/old/581_YiCiZhuYe_canye/CMakeLists.txt diff --git a/src/mission/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.cpp b/src/mission/old/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.cpp similarity index 100% rename from src/mission/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.cpp rename to src/mission/old/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.cpp diff --git a/src/mission/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.h b/src/mission/old/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.h similarity index 98% rename from src/mission/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.h rename to src/mission/old/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.h index f813795..09b3287 100644 --- a/src/mission/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.h +++ b/src/mission/old/581_ZhongHan_dibutiejiao/581_ZhongHan_dibutiejiao.h @@ -38,7 +38,7 @@ void DrawRotatedRect2Mat(cv::Mat& img, cv::RotatedRect rotateRect, cv::Scalar co } //画在结果图上时缩小1/2 -halconResult halconProcess(cv::Mat& sourceImg, TextRendererZH* textPainter, const std::string produce,const std::string& cameraKeyName) { +halconResult halconProcess(cv::Mat& sourceImg, cvPainter* textPainter, const std::string produce,const std::string& cameraKeyName) { halconResult result; try { @@ -173,7 +173,7 @@ bool dibutiejiao_90(std::shared_ptr data, zhongHanFanMianTeJiaoParams* } else cv::resize(sourceImg, Paint, cv::Size(), 0.5, 0.5); - TextRendererZH textPainter(Paint.cols, Paint.rows, 150); + cvPainter textPainter; halconResult result = halconProcess(sourceImg, &textPainter, _detParams->in_Product_Str,data->cameraKeyName); if (result.isNG == true){//NG diff --git a/src/mission/581_ZhongHan_dibutiejiao/CMakeLists.txt b/src/mission/old/581_ZhongHan_dibutiejiao/CMakeLists.txt similarity index 100% rename from src/mission/581_ZhongHan_dibutiejiao/CMakeLists.txt rename to src/mission/old/581_ZhongHan_dibutiejiao/CMakeLists.txt diff --git a/src/mission/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.cpp b/src/mission/old/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.cpp similarity index 99% rename from src/mission/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.cpp rename to src/mission/old/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.cpp index 661e3be..992132e 100644 --- a/src/mission/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.cpp +++ b/src/mission/old/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.cpp @@ -173,7 +173,7 @@ bool TieJiaoZhengMian45(std::shared_ptr data, ZhongHanTieJiaoParams* _ qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); cv::Mat Paint; - Paint.copyTo(data->getOriImg(0)); + data->getOriImg(0).copyTo(Paint); if (Paint.channels() == 1) { cv::cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); } diff --git a/src/mission/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.h b/src/mission/old/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.h similarity index 100% rename from src/mission/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.h rename to src/mission/old/581_ZhongHan_tiejiao_dingbu/581_ZhongHan_tiejiao_dingbu.h diff --git a/src/mission/581_ZhongHan_tiejiao_dingbu/CMakeLists.txt b/src/mission/old/581_ZhongHan_tiejiao_dingbu/CMakeLists.txt similarity index 100% rename from src/mission/581_ZhongHan_tiejiao_dingbu/CMakeLists.txt rename to src/mission/old/581_ZhongHan_tiejiao_dingbu/CMakeLists.txt diff --git a/src/mission/old/CMakeLists.txt b/src/mission/old/CMakeLists.txt new file mode 100644 index 0000000..f9a95fd --- /dev/null +++ b/src/mission/old/CMakeLists.txt @@ -0,0 +1,11 @@ +# CMakeList.txt: 遍历文件夹 添加子项目 + +set(OUR_LIBS "") +SUBDIRENAMES(OUR_LIBS ${CMAKE_CURRENT_SOURCE_DIR} "" OFF) +message("OUR_LIBS :" ${OUR_LIBS}) + + +# 遍历子库 +foreach(dll ${OUR_LIBS}) + add_subdirectory(${dll}) +endforeach(dll ${}) diff --git a/src/mission/opencvPaintAlgo.hpp b/src/mission/opencvPaintAlgo.hpp new file mode 100644 index 0000000..54a9039 --- /dev/null +++ b/src/mission/opencvPaintAlgo.hpp @@ -0,0 +1,401 @@ +#pragma once +#include +#include + +#pragma region 画叉 +static void Cross(cv::Mat& image, const cv::Point& center, int length, int thickness, const cv::Scalar& color = cv::Scalar(255, 255, 255)) { + // 检查输入图像是否有效 + if (image.empty()) { + throw std::invalid_argument("Input image is empty"); + } + + // 计算水平和垂直线段的起点和终点 + cv::Point horizontalStart(center.x - length / 2, center.y); + cv::Point horizontalEnd(center.x + length / 2, center.y); + cv::Point verticalStart(center.x, center.y - length / 2); + cv::Point verticalEnd(center.x, center.y + length / 2); + + // 绘制水平和垂直线段 + cv::line(image, horizontalStart, horizontalEnd, color, thickness); + cv::line(image, verticalStart, verticalEnd, color, thickness); +} +#pragma endregion +//画箭头 长度为负值时为反方向 +static void drawArrow(cv::Mat& Paint, const cv::Point& start, const int& length, const bool& horizonal, const cv::Scalar& color, const int& thinkess, const int& arrowSize) { + + if (horizonal == true) {//线段是否水平 + cv::Point end = cv::Point(start.x + length, start.y); + cv::line(Paint, start, end, color, thinkess); + if (length > 0) {//判断方向 往右 + cv::line(Paint, end, cv::Point(end.x - arrowSize, end.y - arrowSize), color, thinkess); + cv::line(Paint, end, cv::Point(end.x - arrowSize, end.y + arrowSize), color, thinkess); + } + else { + cv::line(Paint, end, cv::Point(end.x + arrowSize, end.y - arrowSize), color, thinkess); + cv::line(Paint, end, cv::Point(end.x + arrowSize, end.y + arrowSize), color, thinkess); + } + } + else {//垂直线段 + cv::Point end = cv::Point(start.x, start.y + length); + cv::line(Paint, start, end, color, thinkess); + if (length > 0) {//判断方向 往下 + cv::line(Paint, end, cv::Point(end.x - arrowSize, end.y - arrowSize), color, thinkess); + cv::line(Paint, end, cv::Point(end.x + arrowSize, end.y - arrowSize), color, thinkess); + } + else { + cv::line(Paint, end, cv::Point(end.x - arrowSize, end.y + arrowSize), color, thinkess); + cv::line(Paint, end, cv::Point(end.x + arrowSize, end.y + arrowSize), color, thinkess); + } + } +} + +void drawRotatedRect(cv::Mat& img, cv::RotatedRect rotateRect, cv::Scalar color, int thickness, int linType) +{ + std::vector boxPts(4); + rotateRect.points(boxPts.data()); + for (int mm = 0; mm < 4; mm++) { + cv::line(img, boxPts[mm], boxPts[(mm + 1) % 4], color, thickness, linType); + } +} + +#pragma region 写中文 +static std::wstring utf8ToWstring(const std::string& utf8Str) { + int len = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, nullptr, 0); + if (len == 0) return L""; + std::wstring wstr(len, L'\0'); + MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &wstr[0], len); + wstr.pop_back(); // remove null terminator + return wstr; +} +// 在图像上绘制中文(仅 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)) { + if (image.channels() != 3) + throw std::runtime_error("Only supports 3-channel images"); + + // 转为 BGRA + cv::Mat img; + cv::cvtColor(image, img, cv::COLOR_BGR2BGRA); + + HDC hdc = GetDC(nullptr); + HDC memDC = CreateCompatibleDC(hdc); + + 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; + + 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()); + + 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); + + SetBkMode(memDC, TRANSPARENT); + SetTextColor(memDC, RGB(color[2], color[1], color[0])); + + TextOutW(memDC, org.x, org.y, utf8ToWstring(text).c_str(), static_cast(utf8ToWstring(text).length())); + + 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; +}; +//写中文 及 缓存绘画数据在调用executePaintJobs后统一绘画 +class TextRendererZH{ +public: + TextRendererZH(const TextRendererZH&) = delete; + TextRendererZH& operator=(const TextRendererZH&) = delete; + + TextRendererZH(TextRendererZH&&) noexcept = default; + TextRendererZH& operator=(TextRendererZH&&) noexcept = default; + TextRendererZH(int width, int height, int fontSize = 20, const std::wstring& fontName = L"微软雅黑") + : width_(width), height_(height), fontSize_(fontSize), fontName_(fontName) + { + screenDC_ = GetDC(nullptr); + if (!screenDC_) { + throw std::runtime_error("Failed to get screen DC"); + } + 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)) { + QMutexLocker locker(&mutex_); + + if (image.channels() != 3) + throw std::runtime_error("TextRendererZH only supports 3-channel BGR images."); + + if (image.cols != width_ || image.rows != height_) { + resize_unsafe(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) { + QMutexLocker locker(&mutex_); + resize_unsafe(width, height); + } + +private: + void resize_unsafe(int width, int height) { + if (width == width_ && height == height_) return; + + releaseResources(); + + width_ = width; + height_ = height; + createResources(); + } + +public: + + struct pointPaintJob { + cv::Point center; + cv::Scalar color; + }; + void appendPaintJob(cv::Point center, cv::Scalar color) { + QMutexLocker locker(&mutex_); + pointPaintJobs.push_back(pointPaintJob{ center,color }); + } + + struct contourPaintJob { + std::vector> contour; + int i; + cv::Scalar color; + int thickness; + int lineType = cv::LINE_8; + cv::InputArray hierarchy = cv::noArray(); + int maxLevel = INT_MAX; + cv::Point offset = cv::Point(); + }; + void appendPaintJob(std::vector> contour, + int i, + cv::Scalar color, int thickness, + int lineType = cv::LINE_8, + cv::InputArray hierarchy = cv::noArray(), + int maxLevel = INT_MAX, cv::Point offset = cv::Point()) { + QMutexLocker locker(&mutex_); + contourPaintJobs.push_back(contourPaintJob{ contour,i,color,thickness,lineType ,hierarchy ,maxLevel ,offset }); + } + + struct linePaintJob { + cv::Point p1; + cv::Point p2; + cv::Scalar color; + int thickness; + }; + void appendPaintJob(cv::Point p1, cv::Point p2, cv::Scalar color, int thickness) { + QMutexLocker locker(&mutex_); + linePaintJobs.push_back(linePaintJob{ p1,p2,color,thickness }); + } + + struct circleAndCorssPaintJob { + cv::Point center; + double radius; + cv::Scalar color; + int thickness; + int type; + }; + //type0 圆 type1 十字 + void appendPaintJob(cv::Point center, double radius,cv::Scalar color, int thickness,int type) { + QMutexLocker locker(&mutex_); + circleAndCorssPaintJobs.push_back(circleAndCorssPaintJob{ center,radius,color,thickness,type }); + } + + struct rectPaintJob { + cv::Rect rect; + cv::Scalar color; + int thickness; + }; + void appendPaintJob(cv::Rect rect, cv::Scalar color,int thickness) { + QMutexLocker locker(&mutex_); + rectPaintJobs.push_back(rectPaintJob{ rect,color,thickness }); + } + + struct textPaintJob { + std::string text; + cv::Point p; + cv::Scalar color; + }; + void appendPaintJob(std::string text, cv::Point p, cv::Scalar color) { + QMutexLocker locker(&mutex_); + textPaintJobs.push_back(textPaintJob{ text,p,color }); + } + + void executePaintJobs(cv::Mat& image,double resizeRate = 1.0) { + QMutexLocker locker(&mutex_); + + if (textPaintJobs.empty() && pointPaintJobs.empty() && circleAndCorssPaintJobs.empty() && rectPaintJobs.empty() && contourPaintJobs.empty() && linePaintJobs.empty()) return; + + if (image.channels() != 3) + throw std::runtime_error("TextRendererZH only supports 3-channel BGR images."); + + if (image.cols != width_ || image.rows != height_) { + resize_unsafe(image.cols, image.rows); + } + + // 绘制点 + for (const auto& job : pointPaintJobs) { + cv::circle(image, job.center, 1, job.color, -1); + } + + // 绘制圆或十字 + for (const auto& job : circleAndCorssPaintJobs) { + if(job.type == 0) + cv::circle(image, job.center, job.radius, job.color, job.thickness); + else + cv::drawMarker(image, job.center, job.color, cv::MARKER_CROSS, job.radius, job.thickness); + } + + // 绘制点集 + for (const auto& job : contourPaintJobs) { + cv::drawContours(image, job.contour , job.i, job.color, job.thickness, job.lineType, job.hierarchy, job.maxLevel, job.offset); + } + + // 绘制线段 + for (const auto& job : linePaintJobs) { + cv::line(image, job.p1, job.p2, job.color, job.thickness); + } + + // 绘制矩形 + for (const auto& job : rectPaintJobs) { + cv::rectangle(image, job.rect, job.color, job.thickness); + } + + // 绘制文本(需要GDI处理) + if (!textPaintJobs.empty()) { + // 只进行一次颜色转换和数据拷贝 + cv::Mat img; + cv::cvtColor(image, img, cv::COLOR_BGR2BGRA); + memcpy(pBits_, img.data, img.total() * img.elemSize()); + + SetBkMode(memDC_, TRANSPARENT); + + // 批量绘制所有文本 + for (const auto& job : textPaintJobs) { + SetTextColor(memDC_, RGB(job.color[2], job.color[1], job.color[0])); + std::wstring wtext = utf8ToWstring(job.text); + TextOutW(memDC_, job.p.x, job.p.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); + } + + // 清空已执行的任务 + contourPaintJobs.clear(); + linePaintJobs.clear(); + pointPaintJobs.clear(); + circleAndCorssPaintJobs.clear(); + rectPaintJobs.clear(); + textPaintJobs.clear(); + } +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; + + // 任务队列 + std::vector textPaintJobs; + std::vector pointPaintJobs; + std::vector circleAndCorssPaintJobs; + std::vector linePaintJobs; + std::vector contourPaintJobs; + std::vector rectPaintJobs; + + // 线程保护 + mutable QMutex mutex_; +}; +#pragma endregion + diff --git a/subModule/Hd_25DCameraVJ_module/Hd_25DCameraVJ_module.h b/subModule/Hd_25DCameraVJ_module/Hd_25DCameraVJ_module.h index 868df2a..a871786 100644 --- a/subModule/Hd_25DCameraVJ_module/Hd_25DCameraVJ_module.h +++ b/subModule/Hd_25DCameraVJ_module/Hd_25DCameraVJ_module.h @@ -37,7 +37,9 @@ public: bool writeData(std::vector&, QByteArray&); bool initParas(QByteArray&); bool run(); - + bool checkStatus() { + return true; + } std::string sTriggerSource; private: bool softwareTriggerStatus = 0; diff --git a/subModule/Hd_CameraModule_3DKeyence/Hd_CameraModule_3DKeyence.h b/subModule/Hd_CameraModule_3DKeyence/Hd_CameraModule_3DKeyence.h index ccdf9fc..eac5629 100644 --- a/subModule/Hd_CameraModule_3DKeyence/Hd_CameraModule_3DKeyence.h +++ b/subModule/Hd_CameraModule_3DKeyence/Hd_CameraModule_3DKeyence.h @@ -60,6 +60,9 @@ public: bool decodeInitData(QByteArray byteArray, QVector& vector_data); bool InitHighSpeed(); + bool checkStatus() { + return true; + } private: //int LJXA_ACQ_Acquire(int lDeviceId, unsigned short* heightImage, unsigned short* luminanceImage, LJXA_ACQ_SETPARAM* setParam, LJXA_ACQ_GETPARAM* getParam); LJX8IF_HIGH_SPEED_PRE_START_REQ* startReq_ptr = nullptr; diff --git a/subModule/Hd_CameraModule_HIK_General/Hd_CameraModule_HIK_General.cpp b/subModule/Hd_CameraModule_HIK_General/Hd_CameraModule_HIK_General.cpp index 21a4b92..8ccf524 100644 --- a/subModule/Hd_CameraModule_HIK_General/Hd_CameraModule_HIK_General.cpp +++ b/subModule/Hd_CameraModule_HIK_General/Hd_CameraModule_HIK_General.cpp @@ -1,10 +1,12 @@ #include "Hd_CameraModule_HIK_General.h" +#include using namespace std; #include #include #include #include #include +#include enum statusEnum { CONNECTED, RECONNECTING, @@ -29,7 +31,39 @@ int cameraIndex = -1; QString lastError; QQueue queuepic; QMutex* n_mutex = new QMutex(); - +class callbackCollectTimeoutTimerClass : public QThread { +public: + void run() { + runFlag.store(true); + counter = 0; + while (runFlag.load() && counter < interval.load()) { + QThread::msleep(ontTimeSleepInterval); + counter += ontTimeSleepInterval; + } + if (runFlag.load() && counter >= interval.load()) + timeoutAction(); + } + void timeoutAction() { + QMutexLocker locker(n_mutex); + if (queuepic.size() > 0) { + queuepic.clear(); + qDebug() << QString::fromStdString(Username) << "上次回调后未取图,已因超时清除缓存,interval" << interval; + std::cout << Username << "上次回调后未取图,已因超时清除缓存,interval" << interval << std::endl; + } + } + void setInterval(int msec) { + interval.store(msec); + } + void stop() { + runFlag.store(false); + } +private: + int counter = 0; + std::atomic runFlag = false; + std::atomic interval = 1500; + int ontTimeSleepInterval = 10; +}; +callbackCollectTimeoutTimerClass callbackCollectTimeoutTimer; void* handle = NULL; void CloseDevice(); @@ -183,6 +217,7 @@ void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser){ void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFrameInfo0, void* pUser0) { Q_UNUSED(pUser0) + callbackCollectTimeoutTimer.stop(); int frameNum = 0; double time_Start = (double)clock(); QMutexLocker locker(n_mutex); @@ -245,12 +280,14 @@ void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFra srcImage = cv::Mat(5, 5, CV_8UC1).setTo(0); } queuepic.enqueue(srcImage); + callbackCollectTimeoutTimer.start(); if (queuepic.size() > 10) queuepic.dequeue(); pData0 = { 0 }; double time_End = (double)clock(); qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getImg,Time:" << (time_End - time_Start) << "ms" << "--CameraName:" << QString::fromStdString(Username) << " imgIndex:" << imgIndex << "nFrameNum : " << frameNum; + } //关闭设备 @@ -280,10 +317,15 @@ void CloseDevice() handle = nullptr; } +Hd_CameraModule_HIK_General::Hd_CameraModule_HIK_General() { +} Hd_CameraModule_HIK_General::~Hd_CameraModule_HIK_General() { runFlag = false; + + // 防止定时线程在资源析构后仍访问队列和互斥锁 + callbackCollectTimeoutTimer.stop(); // 等待重连线程结束 int waitCount = 0; @@ -309,10 +351,10 @@ bool Hd_CameraModule_HIK_General::readData(std::vector& mat, QByteArray { return false; } + callbackCollectTimeoutTimer.stop(); QMutexLocker locker(n_mutex); cv::Mat img = queuepic.dequeue(); mat.push_back(img); - qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg readData--CameraName:" << QString::fromStdString(Username) << "--imgIndex : " << imgIndex; return true; } @@ -332,11 +374,11 @@ bool Hd_CameraModule_HIK_General::writeData(std::vector& mat, QByteArra data = lastError.toUtf8(); return true; } - //if (data == "clearCache") { - // QMutexLocker locker(n_mutex); - // queuepic.clear(); - // return true; - //} + if (data == "clearCache") { + QMutexLocker locker(n_mutex); + queuepic.clear(); + return true; + } if (data.contains("exposure")){ auto commandList = data.split('_'); QMutexLocker locker(n_mutex); @@ -408,7 +450,7 @@ bool Hd_CameraModule_HIK_General::run() bool Hd_CameraModule_HIK_General::checkStatus() { //status = moduleStatus; - return status; + return cameraStatus == CONNECTED; } diff --git a/subModule/Hd_CameraModule_HIK_General/Hd_CameraModule_HIK_General.h b/subModule/Hd_CameraModule_HIK_General/Hd_CameraModule_HIK_General.h index 2483799..e63e81a 100644 --- a/subModule/Hd_CameraModule_HIK_General/Hd_CameraModule_HIK_General.h +++ b/subModule/Hd_CameraModule_HIK_General/Hd_CameraModule_HIK_General.h @@ -59,7 +59,7 @@ bool IsColor(MvGvspPixelType enType) class Hd_CameraModule_HIK_General :public dsModuleBase { public: - Hd_CameraModule_HIK_General() {}; + Hd_CameraModule_HIK_General(); virtual ~Hd_CameraModule_HIK_General(); //从类中读数据到实例对象 bool readData(std::vector& mat, QByteArray& data); diff --git a/subModule/Hd_CameraModule_HIK_General_debouncing/Hd_CameraModule_HIK_General.cpp b/subModule/Hd_CameraModule_HIK_General_debouncing/Hd_CameraModule_HIK_General.cpp index 6d8215b..6f0a114 100644 --- a/subModule/Hd_CameraModule_HIK_General_debouncing/Hd_CameraModule_HIK_General.cpp +++ b/subModule/Hd_CameraModule_HIK_General_debouncing/Hd_CameraModule_HIK_General.cpp @@ -31,7 +31,40 @@ int cameraIndex = -1; QString lastError; QQueue queuepic; QMutex* n_mutex = new QMutex(); - +class callbackCollectTimeoutTimerClass : public QThread { +public: + void run() { + runFlag.store(true, std::memory_order_release); + counter = 0; + while (runFlag.load() && counter < interval.load()) { + QThread::msleep(ontTimeSleepInterval); + counter += ontTimeSleepInterval; + } + if (runFlag.load() && counter >= interval.load()) + timeoutAction(); + } + void timeoutAction() { + QMutexLocker locker(n_mutex); + if (queuepic.size() > 0) { + queuepic.clear(); + qDebug() << QString::fromStdString(Username) << "上次回调后未取图,已因超时清除缓存,interval" << interval; + std::cout << Username << "上次回调后未取图,已因超时清除缓存,interval" << interval << std::endl; + } + } + void setInterval(int msec) { + interval.store(msec); + } + void stop() { + runFlag.store(false); + terminate(); + } +private: + int counter = 0; + std::atomic runFlag = false; + std::atomic interval = 1500; + int ontTimeSleepInterval = 10; +}; +callbackCollectTimeoutTimerClass callbackCollectTimeoutTimer; void* handle = NULL; void CloseDevice(); @@ -189,7 +222,7 @@ void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFra return; } eTimer.start(); - + callbackCollectTimeoutTimer.stop(); Q_UNUSED(pUser0) int frameNum = 0; double time_Start = (double)clock(); @@ -252,6 +285,7 @@ void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFra { srcImage = cv::Mat(5, 5, CV_8UC1).setTo(0); } + callbackCollectTimeoutTimer.start(); queuepic.enqueue(srcImage); if (queuepic.size() > 10) queuepic.dequeue(); @@ -288,7 +322,9 @@ void CloseDevice() handle = nullptr; } - +Hd_CameraModule_HIK_General::Hd_CameraModule_HIK_General() { + \ +} Hd_CameraModule_HIK_General::~Hd_CameraModule_HIK_General() { runFlag = false; @@ -340,11 +376,11 @@ bool Hd_CameraModule_HIK_General::writeData(std::vector& mat, QByteArra data = lastError.toUtf8(); return true; } - /* if (data == "clearCache") { + if (data == "clearCache") { QMutexLocker locker(n_mutex); queuepic.clear(); return true; - }*/ + } if (data.contains("exposure")) { auto commandList = data.split('_'); QMutexLocker locker(n_mutex); @@ -416,7 +452,7 @@ bool Hd_CameraModule_HIK_General::run() bool Hd_CameraModule_HIK_General::checkStatus() { //status = moduleStatus; - return status; + return cameraStatus == CONNECTED; } diff --git a/subModule/Hd_CameraModule_HIK_General_debouncing/Hd_CameraModule_HIK_General.h b/subModule/Hd_CameraModule_HIK_General_debouncing/Hd_CameraModule_HIK_General.h index 2483799..e63e81a 100644 --- a/subModule/Hd_CameraModule_HIK_General_debouncing/Hd_CameraModule_HIK_General.h +++ b/subModule/Hd_CameraModule_HIK_General_debouncing/Hd_CameraModule_HIK_General.h @@ -59,7 +59,7 @@ bool IsColor(MvGvspPixelType enType) class Hd_CameraModule_HIK_General :public dsModuleBase { public: - Hd_CameraModule_HIK_General() {}; + Hd_CameraModule_HIK_General(); virtual ~Hd_CameraModule_HIK_General(); //从类中读数据到实例对象 bool readData(std::vector& mat, QByteArray& data); diff --git a/subModule/Hd_CameraModule_HIK_PCIE/Hd_CameraModule_HIK_PCIE.cpp b/subModule/Hd_CameraModule_HIK_PCIE/Hd_CameraModule_HIK_PCIE.cpp index 52773d0..fa75a98 100644 --- a/subModule/Hd_CameraModule_HIK_PCIE/Hd_CameraModule_HIK_PCIE.cpp +++ b/subModule/Hd_CameraModule_HIK_PCIE/Hd_CameraModule_HIK_PCIE.cpp @@ -5,6 +5,7 @@ using namespace std; #include #include #include +#include enum statusEnum { CONNECTED, RECONNECTING, @@ -30,7 +31,39 @@ int cameraIndex = -1; QString lastError; QQueue queuepic; QMutex* n_mutex = new QMutex(); - +class callbackCollectTimeoutTimerClass : public QThread { +public: + void run() { + runFlag.store(true); + counter = 0; + while (runFlag.load() && counter < interval.load()) { + QThread::msleep(ontTimeSleepInterval); + counter += ontTimeSleepInterval; + } + if (runFlag.load() && counter >= interval.load()) + timeoutAction(); + } + void timeoutAction() { + QMutexLocker locker(n_mutex); + if (queuepic.size() > 0) { + queuepic.clear(); + qDebug() << QString::fromStdString(Username) << "上次回调后未取图,已因超时清除缓存,interval" << interval; + std::cout << Username << "上次回调后未取图,已因超时清除缓存,interval" << interval << std::endl; + } + } + void setInterval(int msec) { + interval.store(msec); + } + void stop() { + runFlag.store(false); + } +private: + int counter = 0; + std::atomic runFlag = false; + std::atomic interval = 1500; + int ontTimeSleepInterval = 10; +}; +callbackCollectTimeoutTimerClass callbackCollectTimeoutTimer; void* handle = NULL; void CloseDevice(); @@ -184,7 +217,8 @@ void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser) { void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFrameInfo0, void* pUser0) { Q_UNUSED(pUser0) - int frameNum = 0; + callbackCollectTimeoutTimer.stop(); + int frameNum = 0; double time_Start = (double)clock(); QMutexLocker locker(n_mutex); cv::Mat srcImage = cv::Mat(); @@ -245,7 +279,7 @@ void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFra { srcImage = cv::Mat(5, 5, CV_8UC1).setTo(0); } - + callbackCollectTimeoutTimer.start(); queuepic.push_back(srcImage); pData0 = { 0 }; double time_End = (double)clock(); @@ -253,11 +287,6 @@ void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFra << "--CameraName:" << QString::fromStdString(Username) << " imgIndex:" << imgIndex << "nFrameNum : " << frameNum; } -Hd_CameraModule_HIK_hard_PCIE::Hd_CameraModule_HIK_hard_PCIE() -{ - //m_thread = new MyThread; - //m_mutex = new QMutex(); -} //关闭设备 void CloseDevice() { @@ -272,8 +301,11 @@ void CloseDevice() //nRet = MV_CC_CloseDevice(handle); //nRet = MV_CC_DestroyHandle(handle); } +Hd_CameraModule_HIK_hard_PCIE::Hd_CameraModule_HIK_hard_PCIE() { +} Hd_CameraModule_HIK_hard_PCIE::~Hd_CameraModule_HIK_hard_PCIE() { + callbackCollectTimeoutTimer.stop(); CloseDevice(); if (n_mutex) { @@ -298,6 +330,7 @@ bool Hd_CameraModule_HIK_hard_PCIE::readData(std::vector& mat, QByteArr { return false; } + callbackCollectTimeoutTimer.stop(); QMutexLocker locker(n_mutex); cv::Mat img = queuepic.dequeue(); mat.push_back(img); diff --git a/subModule/Hd_VirtualCamera/Hd_VirtualCamera.cpp b/subModule/Hd_VirtualCamera/Hd_VirtualCamera.cpp index 12d9473..5a8e42b 100644 --- a/subModule/Hd_VirtualCamera/Hd_VirtualCamera.cpp +++ b/subModule/Hd_VirtualCamera/Hd_VirtualCamera.cpp @@ -1,14 +1,46 @@ -#include "Hd_VirtualCamera.h" +#include "Hd_VirtualCamera.h" #include QQueue> queuePic; QMutex* n_mutex = new QMutex(); +#include - +class callbackCollectTimeoutTimerClass : public QThread { +public: + void run() { + runFlag = true; + counter = 0; + while (runFlag && counter < interval) { + QThread::msleep(ontTimeSleepInterval); + counter += ontTimeSleepInterval; + } + if (counter >= interval) + timeoutAction(); + } + void timeoutAction() { + QMutexLocker locker(n_mutex); + if (queuePic.size() > 0) { + queuePic.clear(); + qDebug() << "上次回调后未取图,已因超时清除缓存,interval" << interval; + std::cout << "上次回调后未取图,已因超时清除缓存,interval" << interval << std::endl; + } + } + void setInterval(int msec) { + interval = msec; + } + void stop() { + runFlag = false; + } +private: + int counter = 0; + std::atomic runFlag = false; + std::atomic interval = 1500; + int ontTimeSleepInterval = 10; +}; +callbackCollectTimeoutTimerClass callbackCollectTimeoutTimer; Hd_VirtualCamera::Hd_VirtualCamera() { - //m_thread = new MyThread; - //m_mutex = new QMutex(); + } Hd_VirtualCamera::~Hd_VirtualCamera() @@ -29,7 +61,7 @@ bool Hd_VirtualCamera::readData(std::vector& mat, QByteArray& data) Q_UNUSED(data) - int cnt = 0; + int cnt = 0; while (queuePic.empty()) { cnt++; @@ -40,6 +72,7 @@ bool Hd_VirtualCamera::readData(std::vector& mat, QByteArray& data) return false; } } + callbackCollectTimeoutTimer.stop(); n_mutex->lock(); vector dstMat = queuePic.dequeue(); n_mutex->unlock(); @@ -66,6 +99,7 @@ bool Hd_VirtualCamera::initParas(QByteArray&n) bool Hd_VirtualCamera::run()//����� { + callbackCollectTimeoutTimer.start(); QThread::msleep(1000); n_mutex->lock(); if (cameraName == "25D-1" || cameraName == "25D-2") { diff --git a/subModule/Hd_VirtualCamera/Hd_VirtualCamera.h b/subModule/Hd_VirtualCamera/Hd_VirtualCamera.h index 4bd64b5..62505f5 100644 --- a/subModule/Hd_VirtualCamera/Hd_VirtualCamera.h +++ b/subModule/Hd_VirtualCamera/Hd_VirtualCamera.h @@ -20,7 +20,9 @@ public: bool writeData(std::vector&, QByteArray&); bool initParas(QByteArray&); bool run(); - + bool checkStatus() { + return false; + } std::string sTriggerSource; private: bool softwareTriggerStatus = 0;