From fc0f4f15670cd3052e597f028c61da4807e85281 Mon Sep 17 00:00:00 2001 From: rci <15089880081@gmail.com> Date: Thu, 24 Oct 2024 10:13:21 +0800 Subject: [PATCH] backup --- .../Hd_AI_algo_fangBaoFa/CMakeLists.txt | 71 + .../Hd_AI_algo_fangBaoFa.cpp | 1094 ++++++++++++++++ .../Hd_AI_algo_fangBaoFa.h | 114 ++ BaseModules/Hd_AI_algo_fangBaoFa/base.h | 69 + .../CMakeLists.txt | 71 + .../Hd_AI_algo_jiZhuCeLiangMain.cpp | 1146 +++++++++++++++++ .../Hd_AI_algo_jiZhuCeLiangMain.h | 113 ++ .../Hd_AI_algo_jiZhuCeLiangMain/base.h | 69 + .../sharedMem_Main.h | 59 + .../铝极_要比铜极先初始化 | 0 .../Hd_AI_algo_jiZhuCeLiangSub/CMakeLists.txt | 71 + .../Hd_AI_algo_jiZhuCeLiangSub.cpp | 1087 ++++++++++++++++ .../Hd_AI_algo_jiZhuCeLiangSub.h | 114 ++ BaseModules/Hd_AI_algo_jiZhuCeLiangSub/base.h | 69 + .../sharedMen_Sub.h | 41 + BaseModules/Hd_AI_algo_jiZhuCeLiangSub/铜极 | 0 .../Hd_AI_algo_maJiaReRong/CMakeLists.txt | 71 + .../Hd_AI_algo_maJiaReRong.cpp | 1079 ++++++++++++++++ .../Hd_AI_algo_maJiaReRong.h | 114 ++ BaseModules/Hd_AI_algo_maJiaReRong/base.h | 69 + .../Hd_LogicDll_module/Logic_hdataprocess.cpp | 8 +- CMakeLists.txt | 4 +- env/halconAlgo/dll/Inspection.dll | Bin 0 -> 435712 bytes env/halconAlgo/include/algo.h | 375 ++++++ env/halconAlgo/include/unionCal.h | 89 ++ env/halconAlgo/lib/Inspection.lib | Bin 0 -> 5442 bytes env/myTools/include/myTools.h | 6 +- 27 files changed, 5994 insertions(+), 9 deletions(-) create mode 100644 BaseModules/Hd_AI_algo_fangBaoFa/CMakeLists.txt create mode 100644 BaseModules/Hd_AI_algo_fangBaoFa/Hd_AI_algo_fangBaoFa.cpp create mode 100644 BaseModules/Hd_AI_algo_fangBaoFa/Hd_AI_algo_fangBaoFa.h create mode 100644 BaseModules/Hd_AI_algo_fangBaoFa/base.h create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangMain/CMakeLists.txt create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangMain/Hd_AI_algo_jiZhuCeLiangMain.cpp create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangMain/Hd_AI_algo_jiZhuCeLiangMain.h create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangMain/base.h create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangMain/sharedMem_Main.h create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangMain/铝极_要比铜极先初始化 create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangSub/CMakeLists.txt create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangSub/Hd_AI_algo_jiZhuCeLiangSub.cpp create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangSub/Hd_AI_algo_jiZhuCeLiangSub.h create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangSub/base.h create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangSub/sharedMen_Sub.h create mode 100644 BaseModules/Hd_AI_algo_jiZhuCeLiangSub/铜极 create mode 100644 BaseModules/Hd_AI_algo_maJiaReRong/CMakeLists.txt create mode 100644 BaseModules/Hd_AI_algo_maJiaReRong/Hd_AI_algo_maJiaReRong.cpp create mode 100644 BaseModules/Hd_AI_algo_maJiaReRong/Hd_AI_algo_maJiaReRong.h create mode 100644 BaseModules/Hd_AI_algo_maJiaReRong/base.h create mode 100644 env/halconAlgo/dll/Inspection.dll create mode 100644 env/halconAlgo/include/algo.h create mode 100644 env/halconAlgo/include/unionCal.h create mode 100644 env/halconAlgo/lib/Inspection.lib diff --git a/BaseModules/Hd_AI_algo_fangBaoFa/CMakeLists.txt b/BaseModules/Hd_AI_algo_fangBaoFa/CMakeLists.txt new file mode 100644 index 0000000..be2265d --- /dev/null +++ b/BaseModules/Hd_AI_algo_fangBaoFa/CMakeLists.txt @@ -0,0 +1,71 @@ +# 获取获取当前目录名 +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" "halconAlgo" "myTools" "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( + + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) + + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC ON) +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 REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS network REQUIRED) +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES LINKER_LANGUAGE C + 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}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " model end : " ${CURRENT_FOLDER}:${RUN_TIME}) diff --git a/BaseModules/Hd_AI_algo_fangBaoFa/Hd_AI_algo_fangBaoFa.cpp b/BaseModules/Hd_AI_algo_fangBaoFa/Hd_AI_algo_fangBaoFa.cpp new file mode 100644 index 0000000..b3fea4a --- /dev/null +++ b/BaseModules/Hd_AI_algo_fangBaoFa/Hd_AI_algo_fangBaoFa.cpp @@ -0,0 +1,1094 @@ +#include "Hd_AI_algo_fangBaoFa.h" +#include + +#include +#include +#include +#include +#include +#include +using namespace cv; +//原图保存文件夹s + QString oriImgSaveDir = "D:/601线/软连接CCD/防爆阀CCD/相机1/原图/"; +//原图class图保存文件夹 + QString classImgSaveDir = "D:/601线/软连接CCD/防爆阀CCD/相机1/class图/"; +//结果保存文件夹 + QString resImgSaveDir = "D:/601线/软连接CCD/防爆阀CCD/相机1/结果图/"; + int algoResult = 0;//0 OK 1 NG +//切图的class的width,根据项目实际设置! + int sizeW = 150; +//切图的class的height,根据项目实际设置! + int sizeH = 150; +//切图的class的width,根据项目实际设置!0为黑,255为白; + int fillColour = 0; + + +//检测线程锁 + QMutex* detect_mutex; +//保存线程锁 + QMutex* saveStruct_mutex; +//检测线程运行标志 + bool ifdetectThread = false; + + QString cameraKey_index = ""; + + //初始化参数列表 + QByteArrayList initValueList; + + static void createDirectoryIfNotExists(const QString& path) { + QDir dir; + dir.mkpath(path); + } + static std::string getCurrentTime() { + + // 获取当前时间点 + auto now = std::chrono::system_clock::now(); + + // 转换为字符串 + auto currentTime = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 100; + std::tm* localTime = std::localtime(¤tTime); + std::stringstream ss; + ss << std::put_time(localTime, "%H-%M-%S") << "-" << std::setfill('0') << std::setw(2) << ms.count(); + return ss.str(); + } + static QString getCurrentDate() + { + QDateTime currentDateTime = QDateTime::currentDateTime(); + return currentDateTime.toString("yyyy-MM-dd"); + } + static QString getCurrentHour() + { + QDateTime currentDateTime = QDateTime::currentDateTime(); + return QString::number(currentDateTime.time().hour())+"H"; + } + QString byteArrayToUnicode(const QByteArray array) { + + // state用于保存转换状态,它的成员invalidChars,可用来判断是否转换成功 + // 如果转换成功,则值为0,如果值大于0,则说明转换失败 + QTextCodec::ConverterState state; + // 先尝试使用utf-8的方式把QByteArray转换成QString + QString text = QTextCodec::codecForName("UTF-8")->toUnicode(array.constData(), array.size(), &state); + // 如果转换时无效字符数量大于0,说明编码格式不对 + if (state.invalidChars > 0) + { + // 再尝试使用GBK的方式进行转换,一般就能转换正确(当然也可能是其它格式,但比较少见了) + text = QTextCodec::codecForName("GBK")->toUnicode(array); + } + return text; + } + static cv::Rect findMaxAreaContour(cv::Mat src) + { + int maxIndex = 0, maxArea = 0; + std::vector>contours; + cv::findContours(src, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + if (contours.size() == 0) + return cv::Rect(0, 0, 0, 0); + for (int i = 0; i < contours.size(); i++) + { + int Area = cv::contourArea(contours[i]); + if (Area > maxArea) + { + maxArea = Area; + maxIndex = i; + } + } + if (contours.size() != 0) + return cv::boundingRect(contours[maxIndex]); + return cv::Rect(0, 0, 0, 0); + } + inline std::string utf8_to_gbk(const std::string& str) + { + std::wstring_convert> conv; + std::wstring tmp_wstr = conv.from_bytes(str); + //GBK locale name in windows + const char* GBK_LOCALE_NAME = ".936"; + std::wstring_convert> convert(new std::codecvt_byname(GBK_LOCALE_NAME)); + return convert.to_bytes(tmp_wstr); + } + void getAllFiles(std::string path, std::vector& files, std::string format) + { + intptr_t hFile = 0;//文件句柄 + struct _finddata_t fileinfo;//文件信息 + std::string p; + if ((hFile = _findfirst(p.assign(path).append("\\*" + format).c_str(), &fileinfo)) != -1) //文件存在 + { + do + { + if ((fileinfo.attrib & _A_SUBDIR))//判断是否为文件夹 + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)//文件夹名中不含"."和".." + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name)); //保存文件夹名 + getAllFiles(p.assign(path).append("\\").append(fileinfo.name), files, format); //递归遍历文件夹 + } + } + else + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name));//如果不是文件夹,储存文件名 + } + } while (_findnext(hFile, &fileinfo) == 0); + _findclose(hFile); + } + } + QString geteOriSaveFromat() + { + return oriImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//" + + "ORI_" + cameraKey_index + "_" + QString::fromStdString(getCurrentTime()) + ".bmp"; + } + QString getResSaveFromat() + { + return resImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//" + + "RES_" + cameraKey_index + "_" + QString::fromStdString(getCurrentTime()) + ".png"; + } +// 缺陷信息结构体 +typedef struct { + QString defectName = "";//缺陷名称 + cv::Rect defectRect = { 0,0,0,0 };//缺陷位置 + cv::Scalar defectScalar = cv::Scalar(0, 0, 0);//画框颜色 + double defectArea = 0;//缺陷真实面积 + double defectDepth = 0;//缺陷深度/高度,3D信息 +}defectInfo; + +//Mat转Halcon图像 +void MatToHObject(cv::Mat& image, HalconCpp::HObject* Hobj) +{ + int hgt = image.rows; + int wid = image.cols; + int i; + if (image.type() == CV_8UC3) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + std::vector imgchannel; + split(image, imgchannel); + cv::Mat imgB = imgchannel[0]; + cv::Mat imgG = imgchannel[1]; + cv::Mat imgR = imgchannel[2]; + uchar* dataR = new uchar[hgt * wid]; + uchar* dataG = new uchar[hgt * wid]; + uchar* dataB = new uchar[hgt * wid]; + for (i = 0; i < hgt; i++) + { + memcpy(dataR + wid * i, imgR.data + imgR.step * i, wid); + memcpy(dataG + wid * i, imgG.data + imgG.step * i, wid); + memcpy(dataB + wid * i, imgB.data + imgB.step * i, wid); + } + himage.GenImage3("byte", wid, hgt, dataR, dataG, dataB); + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPtR, * hImgPtG, * hImgPtB; + himage.GetImagePointer3(&hImgPtR, &hImgPtG, &hImgPtB, &tmpType, &tmpW, &tmpH); + + GenImage3(Hobj, "byte", wid, hgt, (Hlong)hImgPtR, (Hlong)hImgPtG, (Hlong)hImgPtB); + delete[]dataR; + delete[]dataG; + delete[]dataB; + dataR = NULL; + dataG = NULL; + dataB = NULL; + } + else if (image.type() == CV_8UC1) + { + //HalconCpp::HImage himage = HalconCpp::HImage(); + + ////uchar* data = new uchar[hgt * wid]; + ////for (i = 0; i < hgt; i++) + ////{ + //// memcpy(data + wid * i, image.data + image.step * i, wid); + ////} + ////gen_image_const + //himage.GenImage1("byte", wid, hgt, image.data); + + //HalconCpp::HString tmpType; + //Hlong tmpW, tmpH; + //void* hImgPt; + //hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + //GenImage1(&Hobj, "byte", wid, hgt, (Hlong)hImgPt); + GenImage1(Hobj, "byte", wid, hgt, (Hlong)image.data); + + /* delete[] data; + data = NULL;*/ + + } + else if (image.type() == CV_16UC1) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + ushort* data = new ushort[hgt * wid * 2]; + + memcpy(data, image.data, hgt * wid * 2); + + + himage.GenImage1("uint2", wid, hgt, data); + + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPt; + hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + GenImage1(Hobj, "uint2", wid, hgt, (Hlong)hImgPt); + + delete[] data; + data = NULL; + + } + else if (image.type() == CV_16SC1) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + short* data = new short[hgt * wid * 2]; + + memcpy(data, image.data, hgt * wid * 2); + + + himage.GenImage1("int2", wid, hgt, data); + + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPt; + hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + GenImage1(Hobj, "int2", wid, hgt, (Hlong)hImgPt); + + delete[] data; + data = NULL; + + } + +} + +//判断文件是否存在,不存在则创建 +bool isFileExist(QString fullFileName) +{ + QFileInfo fileInfo(fullFileName); + if (fileInfo.isFile()) + { + return true; + } + return false; +} + +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 putTextZH(cv::Mat& dst, const char* str, cv::Point org, cv::Scalar color, int fontsize, + const char* fn = "Arial", bool italic = false, bool underline = false) +{ + CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3)); + + + int x, y, r, b; + //坐标点大于图像宽和高直接返回 + if (org.x > dst.cols || org.y > dst.rows) return; + x = org.x < 0 ? -org.x : 0; + y = org.y < 0 ? -org.y : 0; + + + LOGFONTA lf; + lf.lfHeight = -fontsize; //字体高度 + lf.lfWidth = 0; //平均宽度 + lf.lfEscapement = 0; //字符排列角度 + lf.lfOrientation = 0; //字符本身旋转的角度 + lf.lfWeight = 5; //设置字体线条的宽度 + lf.lfItalic = italic; //斜体 + lf.lfUnderline = underline; //下划线 + lf.lfStrikeOut = 0; //是否字符中央加横线 + lf.lfCharSet = DEFAULT_CHARSET; //字符集 + lf.lfOutPrecision = 0; //字体的精确度 + lf.lfQuality = PROOF_QUALITY; //字体质量 + lf.lfPitchAndFamily = 0; //选择字体的间距和字体家族 + strcpy_s(lf.lfFaceName, fn); //字体的名称 + + + HFONT hf = CreateFontIndirectA(&lf); + HDC hdc = CreateCompatibleDC(0); + HFONT holdfont = (HFONT)SelectObject(hdc, hf); + + + int strBaseW = 0, strBaseH = 0; + int singleRow = 0; + char buf[1 << 12]; + strcpy_s(buf, str); + char* bufT[1 << 12]; //这个用于分隔字符串后剩余的字符,可能会超出。 + + + //处理多行 + { + int nnh = 0; + int cw, ch; + + + const char* ln = strtok_s(buf, "\n", bufT); + while (ln != 0) + { + GetStringSize(hdc, ln, &cw, &ch); + strBaseW = max(strBaseW, cw); + strBaseH = max(strBaseH, ch); + + + ln = strtok_s(0, "\n", bufT); + nnh++; + } + singleRow = strBaseH; + strBaseH *= nnh; + } + + + if (org.x + strBaseW < 0 || org.y + strBaseH < 0) + { + SelectObject(hdc, holdfont); + DeleteObject(hf); + DeleteObject(hdc); + return; + } + + + r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1; + b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1; + org.x = org.x < 0 ? 0 : org.x; + org.y = org.y < 0 ? 0 : org.y; + + + BITMAPINFO bmp = { 0 }; + BITMAPINFOHEADER& bih = bmp.bmiHeader; + int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4)); + + + bih.biSize = sizeof(BITMAPINFOHEADER); + bih.biWidth = strBaseW; + bih.biHeight = strBaseH; + bih.biPlanes = 1; + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + bih.biSizeImage = strBaseH * strDrawLineStep; + bih.biClrUsed = 0; + bih.biClrImportant = 0; + + + void* pDibData = 0; + HBITMAP hBmp = CreateDIBSection(hdc, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0); + + + CV_Assert(pDibData != 0); + HBITMAP hOldBmp = (HBITMAP)SelectObject(hdc, hBmp); + + + //color.val[2], color.val[1], color.val[0] + SetTextColor(hdc, RGB(255, 255, 255)); + SetBkColor(hdc, 0); + //SetStretchBltMode(hDC, COLORONCOLOR); + + + strcpy_s(buf, str); + const char* ln = strtok_s(buf, "\n", bufT); + int outTextY = 0; + while (ln != 0) + { + TextOutA(hdc, 0, outTextY, ln, strlen(ln)); + outTextY += singleRow; + ln = strtok_s(0, "\n", bufT); + } + uchar* dstData = (uchar*)dst.data; + int dstStep = dst.step / sizeof(dstData[0]); + unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep; + unsigned char* pStr = (unsigned char*)pDibData + x * 3; + for (int tty = y; tty <= b; ++tty) + { + unsigned char* subImg = pImg + (tty - y) * dstStep; + unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep; + for (int ttx = x; ttx <= r; ++ttx) + { + for (int n = 0; n < dst.channels(); ++n) { + double vtxt = subStr[n] / 255.0; + int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n]; + subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv); + } + + + subStr += 3; + subImg += dst.channels(); + } + } + + + SelectObject(hdc, hOldBmp); + SelectObject(hdc, holdfont); + DeleteObject(hf); + DeleteObject(hBmp); + DeleteDC(hdc); +} + +void drawDefects(cv::Mat& sourceMat, QQueue defectInfoList) +{ + for (int i = 0; i < defectInfoList.size(); i++) + { + defectInfo mdefect = defectInfoList[i]; + + cv::rectangle(sourceMat, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y), + cv::Point(mdefect.defectRect.x + mdefect.defectRect.width, mdefect.defectRect.y + mdefect.defectRect.height), mdefect.defectScalar, 3, 8); + + QByteArray path_arr = mdefect.defectName.toLocal8Bit(); + const char* path = path_arr.data(); + putTextZH(sourceMat, path, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y - 50), mdefect.defectScalar, 50, "微软雅黑"); + } +} + + + +//判断文件夹是否存在,不存在则创建 +void Hd_AI_algo_fangBaoFa::isDirExist(QString fullDirName) +{ + QDir* photo = new QDir; + bool exist = photo->exists(fullDirName); + if (exist) + { + //qDebug() << fullDirName << "::已存在"; + } + else + { + //创建photo文件夹 + //qDebug() << fullDirName << "::不存在,创建"; + photo->mkdir(fullDirName); + } + delete photo; + photo = nullptr; +} + +Hd_AI_algo_fangBaoFa* create() +{ + qDebug() << "[INFO] " << " Hd_AI_algo_fangBaoFa create success!" ; + return new Hd_AI_algo_fangBaoFa; +} + +void destory(Hd_AI_algo_fangBaoFa* ptr) +{ + qDebug() << "[INFO] " << "Hd_AI_algo_fangBaoFa destory success!"; + if(ptr) + { + delete ptr; + ptr = nullptr; + } +} + +Hd_AI_algo_fangBaoFa::Hd_AI_algo_fangBaoFa() +{ + detect_mutex = new QMutex; + saveStruct_mutex = new QMutex; + ifdetectThread = false; + for (int i = 0; i < 4; i++) + { + QString DirPath = "E:/testImg/source" + QString::number(i); + QStringList imgFilters; + imgFilters << "*.jpg" << "*.png" << "*.PNG" << "*.gif" << "*.tif" << "*.jpeg" << "*.bmp"; + QDir qdir(DirPath); + //返回文件名列表 + QStringList localSourceImgsName = qdir.entryList(imgFilters, QDir::Files | QDir::Readable, QDir::Name); + for (int j = 0; j < localSourceImgsName.size(); j++) + { + QString imgPath = DirPath + "/" + localSourceImgsName[j]; + QFile myf(imgPath); + if (myf.exists()) + myf.remove(); + //Sleep(1); + } + } +} + +Hd_AI_algo_fangBaoFa::~Hd_AI_algo_fangBaoFa() +{ + ifdetectThread = false; + Sleep(10); + if (detect_mutex) + { + delete detect_mutex; + detect_mutex = nullptr; + } + if (saveStruct_mutex) + { + delete saveStruct_mutex; + saveStruct_mutex = nullptr; + } + + /*DestroySegObj(pSeg); + DestroyClsObj(pCls);*/ +} +//从类中读数据到实例对象 +bool Hd_AI_algo_fangBaoFa::readData(std::vector&vec_mat,QByteArray &data) +{ + detect_mutex->lock(); + //qDebug() << __FUNCTION__ << " line:" <<__LINE__ << " data len:" << data.length(); + if (resMat.size()>0) + { + vec_mat.push_back(resMat[0]); + resMat.clear(); + } + else + { + if(HikMat.size()>0) + vec_mat.push_back(HikMat[0]); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << " resMat.data err:"; + } + //if (allNum++ % 2 == 0) + // data = "2*3*2";// resByte.dequeue(); + //else + data = "1*" + QByteArray::number(algoResult) + "&+" + QByteArray::number(algoResult);//1OK,2NG + resByte.clear(); + ////之后根据结果类修改 + //if(resIfNg == true) + // data = "NG"; + //else data = "OK"; + detect_mutex->unlock(); + /*if(cameraKey_index.contains('_')) + qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName:" + << cameraKey_index.split("_")[0] << " imgIndex:" << cameraKey_index.split("_")[1]; + else + qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName imgIndex:" << cameraKey_index;*/ + return true; +} +//从类中读数据到实例对象 +bool Hd_AI_algo_fangBaoFa::encodeResultValue(QVector append_value,QByteArray &resultByte) +{ + try + { + std::string sendMsg; + for(int i = 0 ; i < append_value.size();i++) + { + std::string value = append_value.at(1); + //type + int type = 3; + std::string m_type; + m_type.insert(0,4,'1'); + memcpy ( (void*)m_type.c_str(),(void*)&type,4 ); + sendMsg.append(m_type); + + //len + int len = value.length(); + std::string m_len; + m_len.insert(0,4,'1'); + memcpy ( (void*)m_len.c_str(),(void*)&len,4 ); + sendMsg.append(m_len); + + //data x + sendMsg.append(value); + } + resultByte = QByteArray::fromStdString(sendMsg); + } catch (QString ev) + { + qCritical() << "[Error] " << " ev:" << ev; + return false; + } + return true; +} + +int myallNum[4] = {0}; +QMap lastImgMap; +//接收数据 +bool Hd_AI_algo_fangBaoFa::writeData(std::vector& vec_mat, QByteArray &data) +{ + //qDebug() << __FUNCTION__ << "line:"<< __LINE__ << " data len" << data.length() ; + if(vec_mat.empty()) + { + qCritical() << "[Error] " << " vec_mat is null !"; + return false; + } + detect_mutex->lock(); + HikMat.clear(); + for (int i = 0; i < vec_mat.size(); i++) + HikMat.enqueue(vec_mat[i]); + + QByteArray source_savePath; + QByteArray sData = data; + QByteArrayList splitDataList = sData.split(','); + if (splitDataList.size() >= 4) + { + if (splitDataList[2].contains('_')) + cameraKey_index = splitDataList[2]; + QByteArray saveDir = splitDataList[0] + "/" + splitDataList[1] + "/"; + isDirExist(byteArrayToUnicode(saveDir)); //不存在,创建 + QString sdataTime= QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz"); + source_savePath = splitDataList[0] + "/" + splitDataList[1] + "/" + sdataTime.toLocal8Bit() + "-" + splitDataList[2] + "-" + splitDataList[3];// +".bmp"; + } + //int index = -1; + /*if (sData.contains("offline-camera0")) + { + index = 0; + savePath = "E:\\testImg\\source0\\"+QString::number(myallNum[0]++) + ".bmp"; + } + else if (sData.contains("offline-camera1")) + { + index = 1; + savePath = "E:\\testImg\\source1\\" + QString::number(myallNum[1]++) + ".bmp"; + } + else if (sData.contains("offline-camera2")) + { + index = 2; + savePath = "E:\\testImg\\source2\\" + QString::number(myallNum[2]++) + ".bmp"; + } + else if (sData.contains("offline-camera3")) + { + index = 3; + savePath = "E:\\testImg\\source3\\" + QString::number(myallNum[3]++) + ".bmp"; + }*/ + + + createDirectoryIfNotExists(oriImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//"); + addImgToSaveStruct(HikMat[0], "0", geteOriSaveFromat());//保存原图 + //if (!source_savePath.isEmpty()) + //{ + // //std::cout << "try savePath::" << savePath.toStdString() << std::endl; + // //测试相机的, + // //while (HikMat[0].cols > 2000)cv::resize(HikMat[0], HikMat[0], cv::Size(), 0.08, 0.08);' + // for (int i = 0; i < initValueList.size(); i++) + // { + // QString minit = byteArrayToUnicode(initValueList[i]); + // if (initValueList[i].contains(data.split(',')[2].split('_')[0])) + // if (initValueList[i].contains(QString("原图_保存").toLocal8Bit())) + // { + // addImgToSaveStruct(HikMat[0], "0", byteArrayToUnicode(source_savePath + ".bmp"));//保存原图 + // if (HikMat.size() == 2)//3D + // addImgToSaveStruct(HikMat[1], "0", byteArrayToUnicode(source_savePath + ".tif")); + // } + // } + //} + + //if (myallNum[index] == 1) + //{ + // cv::Mat img; + // lastImgMap.insert(index, img); + //} + //else + //{ + // //判断和上一张图,是否重复 + // int dataLength = HikMat[0].total() * HikMat[0].elemSize(); + // bool res = false; + // if (myallNum[index] > 0) + // { + // cv::Mat img = lastImgMap.value(index); + // if (img.total() * img.elemSize() == dataLength) + // { + // res = memcmp(img.data, HikMat[0].data, dataLength) == 0; + // if (res == true) + // std::cout << "res ==true ====!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + // } + // } + //} + //lastImgMap.insert(index, HikMat[0]); + + //or检测:保存路径,拍照面描述,相机key_第几张图,SN_时间戳 + //and检测:保存路径,拍照面描述,相机key_第几张图,相机key_第几张图,SN_时间戳,SN_时间戳 + //汇总:保存路径,拍照面描述,触发信号_序号列表0,SN_时间戳 + QString qstring_data = data; + QStringList datalist = qstring_data.split(','); + + + /*saveKey.enqueue("datalist[0]"); + Dir2D.enqueue("datalist[1]"); + QStringList SN_dataTime; + SN_dataTime = datalist[3].split('_'); + DateTime1 = "SN_dataTime[1]"; + SN.enqueue("SN_dataTime[0]");*/ + //qCritical() << __FUNCTION__ << " line: " << __LINE__ << " data:" << qstring_data; + detect_mutex->unlock(); + + //qDebug() << __FUNCTION__ << "line:" << __LINE__ << "[INFO] " << " data: " << data << " run end"; + //cv::Mat resultDstImg = vec_mat[0].clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + //resultDstImg = pSeg->Evaluate(resultDstImg); + //resultDstImg.release(); + + return true; +} + +//传入initParas函数,格式为:相机key+参数名1#参数值1#参数值2+参数名2#参数值1... +//初始化 +bool Hd_AI_algo_fangBaoFa::initParas(QByteArray& byte) +{ + initValueList = byte.split('+'); + QStringList valueiList; + + if (byte.contains("onlySetParameter")) + { + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,valueList[0]:" << initValueList[0]; + if (initValueList.size() > 1) + for (int i = 1; i < initValueList.size(); i++) + { + QString valuei = byteArrayToUnicode(initValueList[i]); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,lastName:" << initValueList[0] << " ,valuei:" << valuei; + } + return true; + } + + + if (initValueList.size() > 1) + for (int i = 1; i < initValueList.size(); i++) + { + QString valuei = byteArrayToUnicode(initValueList[i]); + valueiList.append(valuei); + } + + /*pSeg = CreateSegObjS(pSeg_modulePath, 128, gpuId); + pCls = CreateClsObjV3(pCls_modulePath, gpuId, 1);*/ + + + //StationNo = vec_ret.at(0).toInt(); + //InitThreads(StationNo, img2D); + //InitModels(StationNo, img2D); + + //运行检测线程 + ifdetectThread = true; + + //线程检测:结果类完成时,使用 + //ifThreadRunDetect = true; + //QFuture future1 = QtConcurrent::run(this, &Hd_AI_algo_fangBaoFa::detectThread); + //运行存图线程 + QFuture future2 = QtConcurrent::run(this, &Hd_AI_algo_fangBaoFa::saveImgThread); + //detectThread(); + //seg_hanHouDingWei = handdleAI::CreateSegObjS("./model/seg-hanHouDingWei.BHD",32,0); + //原图总文件夹 + isDirExist(oriImgSaveDir); + //原图class总文件夹 + isDirExist(classImgSaveDir); + //结果图总文件夹 + isDirExist(resImgSaveDir); + + //qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ; + return true; +} +// 解码 +bool Hd_AI_algo_fangBaoFa::decodeAiValue(QByteArray byte,QVector &vec_ret) +{ + if(byte.isEmpty()) + { + qCritical() << "[Error] " << " byte is null!"; + return false; + } + + /*******************************************/ + byte.remove(0,8); + QByteArray mm_data = byte.mid(0,4); + memcpy((void*)¶mNum,(void*)mm_data.data(), 4); + qDebug() << "[INFO] " << " paramNum:" << paramNum; + byte.remove(0,4); + /*******************************************/ + + while (!byte.isEmpty()) + { + int type = 0; + QByteArray m_type = byte.mid(0,4); + memcpy((void*)&type,(void*)m_type.data(), 4); + byte.remove(0,4); + + int len = 0; + QByteArray m_len = byte.mid(0,4); + memcpy((void*)&len,(void*)m_len.data(), 4); + byte.remove(0,4); + + QByteArray mm_data = byte.mid(0,len); + vec_ret.append(mm_data); + byte.remove(0,len); + } + return true; +} +//---------检测流程,根据实际项目修改--------- +bool Hd_AI_algo_fangBaoFa::AIdetect(cv::Mat img2D, QString mysaveKey, QString myDir2D, QString mySN, QString myDataTime) +{ + try + { + //存图数据加保存队列,加锁,避免数据访问冲突 + saveStruct_mutex->lock(); + saveImgStruct mySave; + Mat sourceImg; + sourceImg = img2D.clone(); + //img2D.copyTo(sourceImg); + //img2D.release(); + QString mysaveKey1 = mysaveKey; //"offlineStation1_off_1" + saveStruct_mutex->unlock(); + + QJsonDocument document; + QJsonObject obj; + obj.insert("result_key", "mysaveKey"); + obj.insert("SN", "mySN"); + obj.insert("DataTime", "myDataTime"); + + + //QFile csvName("test.csv"); + //QStringList lines; + //lines << "class,name\n" << "3,4,5,6,7\n" << "a,b,c,d\n"; + //if (csvName.open(QIODevice::WriteOnly)) //若文件不存在,会自动创建 + //{ + // for (int i = 0; i < lines.size(); i++) + // csvName.write(lines[i].toStdString().c_str()); + // csvName.close(); + //} + //sourceImg = cv::imread("D:\\mySN15-46-33-86.bmp",-1); + //sourceImg = cv::imread("D:/CCD/Image_20241017202132775.bmp", -1); + sourceImg = cv::imread("D:/1/ruanLianJie/fbf/Image_20240816162604747.bmp",-1); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if(Paint.channels() == 1) + cvtColor(Paint, Paint, COLOR_GRAY2BGR); + + HObject ho_Image; + HTuple hv_Pix(0.2); + HTuple hv_Distance_Value; + HTuple hv_Out_Point; + HTuple hv_Make; + MatToHObject(sourceImg,&ho_Image); + _628_3_ExplosionProofValves(ho_Image, hv_Pix, + &hv_Distance_Value,&hv_Out_Point, + &hv_Make); + if(hv_Make.D() == 1) + { + int crossLineLength = 50; + cv::line(Paint, cv::Point(hv_Out_Point[1].D(), hv_Out_Point[0].D()), + cv::Point(hv_Out_Point[1].D() - crossLineLength, hv_Out_Point[0].D()), + cv::Scalar(0, 255, 0), 5); + cv::line(Paint, cv::Point(hv_Out_Point[1].D(), hv_Out_Point[0].D()), + cv::Point(hv_Out_Point[1].D() + crossLineLength, hv_Out_Point[0].D()), + cv::Scalar(0, 255, 0), 5); + cv::line(Paint, cv::Point(hv_Out_Point[1].D(), hv_Out_Point[0].D()), + cv::Point(hv_Out_Point[1].D(), hv_Out_Point[0].D() - crossLineLength), + cv::Scalar(0, 255, 0), 5); + cv::line(Paint, cv::Point(hv_Out_Point[1].D(), hv_Out_Point[0].D()), + cv::Point(hv_Out_Point[1].D(), hv_Out_Point[0].D() + crossLineLength), + cv::Scalar(0, 255, 0), 5); + int crossLineLength2 = 150; + cv::line(Paint, cv::Point(hv_Out_Point[3].D(), hv_Out_Point[2].D()), + cv::Point(hv_Out_Point[3].D() - crossLineLength2, hv_Out_Point[2].D()), + cv::Scalar(0, 255, 0), 5); + cv::line(Paint, cv::Point(hv_Out_Point[3].D(), hv_Out_Point[2].D()), + cv::Point(hv_Out_Point[3].D() + crossLineLength2, hv_Out_Point[2].D()), + cv::Scalar(0, 255, 0), 5); + cv::line(Paint, cv::Point(hv_Out_Point[3].D(), hv_Out_Point[2].D()), + cv::Point(hv_Out_Point[3].D(), hv_Out_Point[2].D() - crossLineLength2), + cv::Scalar(0, 255, 0), 5); + cv::line(Paint, cv::Point(hv_Out_Point[3].D(), hv_Out_Point[2].D()), + cv::Point(hv_Out_Point[3].D(), hv_Out_Point[2].D() + crossLineLength2), + cv::Scalar(0, 255, 0), 5); + putTextHusky(Paint, utf8_to_gbk("间距"+std::to_string(hv_Distance_Value.D())).c_str(), cv::Point(0, 50), cv::Scalar(0, 255, 0), 500); + } + else + putTextHusky(Paint, utf8_to_gbk("算法错误").c_str(), cv::Point(0, 50), cv::Scalar(0, 255, 0), 500); + + + QQueue defectInfoList; + + + detect_mutex->lock(); + + + + + + resMat.push_back(Paint); + QJsonArray detaillists; + //QByteArrayList resQbyte; + for (int i = 0; i < defectInfoList.size(); i++) + { + defectInfo mdefect = defectInfoList[i]; + //resQbyte.push_back(mdefect.defectName.toLocal8Bit()); //中文也可以QString::number(1.1).toUtf8() + //resQbyte.push_back(QString::number(mdefect.defectRect.x).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.y).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.width).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.height).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[0]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[1]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[2]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectArea).toUtf8()); + QJsonObject detailObj; + detailObj.insert("defectName", "mdefect.defectName"); + detailObj.insert("defectRect.x", QString::number(mdefect.defectRect.x)); + detailObj.insert("defectRect.y", QString::number(mdefect.defectRect.y)); + detailObj.insert("defectRect.width", QString::number(mdefect.defectRect.width)); + detailObj.insert("defectRect.height", QString::number(mdefect.defectRect.height)); + detailObj.insert("defectScalar0", QString::number(mdefect.defectScalar[0])); + detailObj.insert("defectScalar1", QString::number(mdefect.defectScalar[1])); + detailObj.insert("defectScalar2", QString::number(mdefect.defectScalar[2])); + detailObj.insert("defectArea", QString::number(mdefect.defectArea)); + detailObj.insert("defectLength", QString::number(sqrt(mdefect.defectRect.width* mdefect.defectRect.width+ mdefect.defectRect.height* mdefect.defectRect.height))); + + detaillists.append(detailObj); + } + + + //使用完删除 + defectInfoList.clear(); + + //往主界面发 + //emit sendResultData(resMat, resQbyte); + //resQbyte.clear(); + + //isDirExist(resImgSaveDir + mysaveKey1 + "/"); + QString qimgsavePath = resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png"; + //存图数据入队,按需 + if (resMat.size() > 0 ) + { + createDirectoryIfNotExists(resImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//"); + addImgToSaveStruct(resMat[0], "0", getResSaveFromat());//保存原图 + } + detect_mutex->unlock(); + + obj.insert("detaillists", detaillists); + obj.insert("qimgsavePath", qimgsavePath); + document.setObject(obj); + //resByte.clear(); + QByteArray qa = document.toJson(); + //resByte .enqueue(qa); + + + //resMat.release(); + sourceImg.release(); + + mySave.out_Mat.resize(0); + mysaveKey1.clear(); + qApp->processEvents(); + + + ////原图和结果都往主界面发? + //std::vector resMat; + //resMat.push_back(img2D); + //resMat.push_back(sourceRectangles); + //QByteArray resByte = mysaveKey.toLocal8Bit(); + //resByte.append(myDir2D.toLocal8Bit()); + //resByte.append(mySN.toLocal8Bit()); + //resByte.append(myDataTime.toLocal8Bit()); + + ////往主界面发送图片和数据 + ////emit writeData(resMat, resByte); + //resMat.resize(0); + //resByte.resize(0); + } + catch (...) + { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + return false; + } + return true; + +} + +//存图加入队列 +void Hd_AI_algo_fangBaoFa::addImgToSaveStruct(cv::Mat img2D, QString mysaveKey, QString savePath) +{ + //存图数据加保存队列,加锁,避免数据访问冲突 + saveStruct_mutex->lock(); + saveImgStruct mySave; + mySave.out_Mat = img2D.clone(); + //img2D.copyTo(mySave.out_Mat); + mySave.saveKey = mysaveKey.toLocal8Bit().toStdString(); + mySave.savePath = savePath.toLocal8Bit().toStdString(); + saveStruct.enqueue(mySave); + saveStruct_mutex->unlock(); +} +void Hd_AI_algo_fangBaoFa::saveImgThread() +{ + while (1) + { + if (!ifdetectThread)return; + _sleep(5); + qApp->processEvents(); + if (saveStruct.size() < 1)continue; + for (int i = 0; i < saveStruct.size(); i++) + { + clock_t start, stop; + start = clock(); + saveStruct_mutex->lock(); + saveImgStruct mysaveStruct = saveStruct.dequeue(); + saveStruct_mutex->unlock(); + stop = clock(); + if(stop-start >1000) + std::cout << "lock stop-start >1000::************************" << stop - start << std::endl; + start = clock(); + if (mysaveStruct.out_Mat.data) + imwrite(mysaveStruct.savePath, mysaveStruct.out_Mat); + //std::cout << "savePath::" << mysaveStruct.savePath << std::endl; + stop = clock(); + if (stop - start > 1000) + std::cout << "imwrite stop-start >1000::--------------------------"<< stop - start << std::endl; + _sleep(1); + } + } +} +//检测线程,应当只运行一次 +void Hd_AI_algo_fangBaoFa::detectThread() +{ + do + { + if (!ifdetectThread)return; + + //此两行代码,避免程序卡死;_sleep的时间根据实际项目调整 + _sleep(2); + qApp->processEvents(); + //取数据加锁,避免数据访问冲突 + detect_mutex->lock(); + //if (HikMat.size() < 1 || saveKey.size() < 1 || Dir2D.size() < 1 || SN.size() < 1 )//|| DateTime1.size() < 1) + //{ + // detect_mutex->unlock(); + // continue; + //} + + cv::Mat sourceImg1 = HikMat[0];// .dequeue(); + QString mysaveKey = "saveKey";// .dequeue(); + QString myDir2D = "Dir2D";// .dequeue(); + QString mySN = "SN";// .dequeue(); + QString myDataTime = "DateTime1"; + detect_mutex->unlock(); + + //判断存图文件夹是否存在,不存在则创建 + { + //原图总文件夹 + isDirExist(oriImgSaveDir + mysaveKey); + //原图class总文件夹 + isDirExist(classImgSaveDir + mysaveKey); + //结果图总文件夹 + isDirExist(resImgSaveDir + mysaveKey); + + } + + //测试相机的, + // while (sourceImg1.cols > 2000)cv::resize(sourceImg1, sourceImg1, cv::Size(), 0.5, 0.5); + + //存图数据入队 + //addImgToSaveStruct(sourceImg1, mysaveKey, oriImgSaveDir + mysaveKey + "/" + mySN + myDataTime + ".bmp"); + + //-------检测流程,根据实际项目修改--------- + AIdetect(sourceImg1, mysaveKey, oriImgSaveDir, mySN, myDataTime); + //sourceImg1.release(); + + mysaveKey.clear(); + myDir2D.clear(); + mySN.clear(); + myDataTime.clear(); + qApp->processEvents(); + + } while (ifThreadRunDetect); + +} + +static int runNum = 0; +bool Hd_AI_algo_fangBaoFa::run() +{ + clock_t start, stop; + start = clock(); + + //Sleep(1000); + detectThread(); + stop = clock(); + qDebug() << __FUNCTION__ << "line:" << __LINE__ << " run start ::runNum::" << runNum++<<" cutTime::"< +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "imgproc/imgproc_c.h" +#include "base.h" +#include "handleAI.h" +#pragma execution_character_set("utf-8") + + + +//保存图片的结构体 +struct saveImgStruct; +//{ +// cv::Mat out_Mat; +// std::string saveKey; //关键字,用于LOG日志 +// std::string savePath; //保存路径 +//}; + +//初始化流程(一次):Hd_AI_algo_fangBaoFa,initParas +//运行流程:writeData主线程把数据传过来,run,readData此线程把数据传主线程 +class Hd_AI_algo_fangBaoFa :public Base +{ + Q_OBJECT +public: + Hd_AI_algo_fangBaoFa(); + virtual ~Hd_AI_algo_fangBaoFa(); + //从类中读数据到实例对象 + bool readData(std::vector&,QByteArray&); + //实例对象把数据写入到类 + bool writeData(std::vector&, QByteArray&); + bool initParas(QByteArray&); + //运行(Logic_taskthread.cpp) + bool run(); + //线程状态 + bool checkStatus();// { return true; }; + + bool encodeResultValue(QVector append_value,QByteArray &resultByte); + bool decodeAiValue(QByteArray byte,QVector &vec_ret); + //---------检测流程,根据实际项目修改--------- + bool AIdetect(cv::Mat img2D, QString saveKey, QString Dir2D, QString SN, QString DataTime); + //handdleAI::pSegmentation* seg_hanHouDingWei; +private: + int allNum = 0; + int StationNo=0; + int img2D = 2; + + bool moduleStatus = true; + + //输入图片队列 + QQueue HikMat; + //输入关键字队列 + QString saveKey="saveKey"; + //结果路径文件夹队列 + QString Dir2D="Dir2D"; + //产品SN队列 + QString SN="SN"; + //产品时间戳队列 + QString DateTime1="DateTime1"; + + int paramNum; + + //是否使用线程检测方式 + bool ifThreadRunDetect = false; + //检测结果图 + std::vector resMat; + //检测结果 + bool resIfNg = false; + + QQueue resByte; + + //检测线程 + void detectThread(); + //存图线程 + void saveImgThread(); + + //保存图片队列 + QQueue saveStruct; + + //判断文件夹是否存在,不存在则创建 + void isDirExist(QString fullDirName); + + //存图加入队列 + void addImgToSaveStruct(cv::Mat img2D, QString saveKey, QString savePath); +}; + +extern "C" +{ + Q_DECL_EXPORT Hd_AI_algo_fangBaoFa * create(); + Q_DECL_EXPORT void destory(Hd_AI_algo_fangBaoFa * ptr); +} +//保存图片的结构体 +struct saveImgStruct +{ + cv::Mat out_Mat; + std::string saveKey; //关键字,用于LOG日志 + std::string savePath; //保存路径 +}; +#endif // Hd_AI_927_2D_module_H diff --git a/BaseModules/Hd_AI_algo_fangBaoFa/base.h b/BaseModules/Hd_AI_algo_fangBaoFa/base.h new file mode 100644 index 0000000..00bd6b2 --- /dev/null +++ b/BaseModules/Hd_AI_algo_fangBaoFa/base.h @@ -0,0 +1,69 @@ +#ifndef __BASECLASS_H__ +#define __BASECLASS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Base :public QObject +{ + Q_OBJECT +public: + bool init_flag; //是否初始化,false,true + bool status = true; //相机状态(判断相机是否断开连接) + std::string paras; //传输数据 + std::vector m_mat; //传输图片 + std::string moduleName; //模块名称 +public: + Base(){} + virtual ~Base(){} + virtual bool readData(std::vector&,QByteArray&){return true;} + virtual bool writeData(std::vector&,QByteArray&){return true;} + virtual bool initParas(QByteArray&){return true;} + virtual bool run() {return true;} + virtual bool checkStatus() { return true; } + +//signals: + //QByteArrayList,根据实际项目进行解析;因不确定每个参数的长度,固用队列的方式。每个QByteArray表示一个值,使用时先转换为QString + //参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double) + //void sendResultData(cv::Mat resMat, QJsonObject resQbj); +}; + +/* +//QJsonObject 转QByteArray +{ + QJsonDocument document; + QJsonObject obj; + obj.insert(password, "pwd"); + obj.insert(userName, "usr"); + document.setObject(obj); + QByteArray abyte = document.toJson(QJsonDocument::Compact); +} + +//QByteArray转QJsonObject +{ + QByteArray ba = reply->readAll(); + QJsonDocument doc = QJsonDocument::fromJson(ba); + QJsonObject obj = doc.object(); +} +*/ +/* +//从类中读数据到实例对象 +virtual bool readData(std::vector&,QByteArray&){return true;} + + + + + + + + +*/ + +#endif diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/CMakeLists.txt b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/CMakeLists.txt new file mode 100644 index 0000000..be2265d --- /dev/null +++ b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/CMakeLists.txt @@ -0,0 +1,71 @@ +# 获取获取当前目录名 +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" "halconAlgo" "myTools" "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( + + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) + + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC ON) +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 REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS network REQUIRED) +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES LINKER_LANGUAGE C + 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}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " model end : " ${CURRENT_FOLDER}:${RUN_TIME}) diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/Hd_AI_algo_jiZhuCeLiangMain.cpp b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/Hd_AI_algo_jiZhuCeLiangMain.cpp new file mode 100644 index 0000000..02340e5 --- /dev/null +++ b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/Hd_AI_algo_jiZhuCeLiangMain.cpp @@ -0,0 +1,1146 @@ +#include "Hd_AI_algo_jiZhuCeLiangMain.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include "sharedMem_Main.h" +using namespace cv; + +//原图保存文件夹s + QString oriImgSaveDir = "D:/601线/软连接CCD/极柱测量CCD/原图/"; +//原图class图保存文件夹 + QString classImgSaveDir = "D:/601线/软连接CCD/极柱测量CCD/class图/"; +//结果保存文件夹 + QString resImgSaveDir = "D:/601线/软连接CCD/极柱测量CCD/结果图/"; + int algoResult = 0;//0 OK 1 NG +//切图的class的width,根据项目实际设置! + int sizeW = 150; +//切图的class的height,根据项目实际设置! + int sizeH = 150; +//切图的class的width,根据项目实际设置!0为黑,255为白; + int fillColour = 0; + + +//检测线程锁 + QMutex* detect_mutex; +//保存线程锁 + QMutex* saveStruct_mutex; +//检测线程运行标志 + bool ifdetectThread = false; + + QString cameraKey_index = ""; + + //初始化参数列表 + QByteArrayList initValueList; + + static void createDirectoryIfNotExists(const QString& path) { + QDir dir; + dir.mkpath(path); + } + static std::string getCurrentTime() { + + // 获取当前时间点 + auto now = std::chrono::system_clock::now(); + + // 转换为字符串 + auto currentTime = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 100; + std::tm* localTime = std::localtime(¤tTime); + std::stringstream ss; + ss << std::put_time(localTime, "%H-%M-%S") << "-" << std::setfill('0') << std::setw(2) << ms.count(); + return ss.str(); + } + static QString getCurrentDate() + { + QDateTime currentDateTime = QDateTime::currentDateTime(); + return currentDateTime.toString("yyyy-MM-dd"); + } + static QString getCurrentHour() + { + QDateTime currentDateTime = QDateTime::currentDateTime(); + return QString::number(currentDateTime.time().hour())+"H"; + } + QString byteArrayToUnicode(const QByteArray array) { + + // state用于保存转换状态,它的成员invalidChars,可用来判断是否转换成功 + // 如果转换成功,则值为0,如果值大于0,则说明转换失败 + QTextCodec::ConverterState state; + // 先尝试使用utf-8的方式把QByteArray转换成QString + QString text = QTextCodec::codecForName("UTF-8")->toUnicode(array.constData(), array.size(), &state); + // 如果转换时无效字符数量大于0,说明编码格式不对 + if (state.invalidChars > 0) + { + // 再尝试使用GBK的方式进行转换,一般就能转换正确(当然也可能是其它格式,但比较少见了) + text = QTextCodec::codecForName("GBK")->toUnicode(array); + } + return text; + } + static cv::Rect findMaxAreaContour(cv::Mat src) + { + int maxIndex = 0, maxArea = 0; + std::vector>contours; + cv::findContours(src, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + if (contours.size() == 0) + return cv::Rect(0, 0, 0, 0); + for (int i = 0; i < contours.size(); i++) + { + int Area = cv::contourArea(contours[i]); + if (Area > maxArea) + { + maxArea = Area; + maxIndex = i; + } + } + if (contours.size() != 0) + return cv::boundingRect(contours[maxIndex]); + return cv::Rect(0, 0, 0, 0); + } + inline std::string utf8_to_gbk(const std::string& str) + { + std::wstring_convert> conv; + std::wstring tmp_wstr = conv.from_bytes(str); + //GBK locale name in windows + const char* GBK_LOCALE_NAME = ".936"; + std::wstring_convert> convert(new std::codecvt_byname(GBK_LOCALE_NAME)); + return convert.to_bytes(tmp_wstr); + } + void getAllFiles(std::string path, std::vector& files, std::string format) + { + intptr_t hFile = 0;//文件句柄 + struct _finddata_t fileinfo;//文件信息 + std::string p; + if ((hFile = _findfirst(p.assign(path).append("\\*" + format).c_str(), &fileinfo)) != -1) //文件存在 + { + do + { + if ((fileinfo.attrib & _A_SUBDIR))//判断是否为文件夹 + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)//文件夹名中不含"."和".." + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name)); //保存文件夹名 + getAllFiles(p.assign(path).append("\\").append(fileinfo.name), files, format); //递归遍历文件夹 + } + } + else + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name));//如果不是文件夹,储存文件名 + } + } while (_findnext(hFile, &fileinfo) == 0); + _findclose(hFile); + } + } + QString geteOriSaveFromat() + { + return oriImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//" + + "ORI_" + cameraKey_index + "_" + QString::fromStdString(getCurrentTime()) + ".bmp"; + } + QString getResSaveFromat() + { + return resImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//" + + "RES_" + cameraKey_index + "_" + QString::fromStdString(getCurrentTime()) + ".png"; + } +// 缺陷信息结构体 +typedef struct { + QString defectName = "";//缺陷名称 + cv::Rect defectRect = { 0,0,0,0 };//缺陷位置 + cv::Scalar defectScalar = cv::Scalar(0, 0, 0);//画框颜色 + double defectArea = 0;//缺陷真实面积 + double defectDepth = 0;//缺陷深度/高度,3D信息 +}defectInfo; + +//Mat转Halcon图像 +void MatToHObject(cv::Mat& image, HalconCpp::HObject* Hobj) +{ + int hgt = image.rows; + int wid = image.cols; + int i; + if (image.type() == CV_8UC3) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + std::vector imgchannel; + split(image, imgchannel); + cv::Mat imgB = imgchannel[0]; + cv::Mat imgG = imgchannel[1]; + cv::Mat imgR = imgchannel[2]; + uchar* dataR = new uchar[hgt * wid]; + uchar* dataG = new uchar[hgt * wid]; + uchar* dataB = new uchar[hgt * wid]; + for (i = 0; i < hgt; i++) + { + memcpy(dataR + wid * i, imgR.data + imgR.step * i, wid); + memcpy(dataG + wid * i, imgG.data + imgG.step * i, wid); + memcpy(dataB + wid * i, imgB.data + imgB.step * i, wid); + } + himage.GenImage3("byte", wid, hgt, dataR, dataG, dataB); + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPtR, * hImgPtG, * hImgPtB; + himage.GetImagePointer3(&hImgPtR, &hImgPtG, &hImgPtB, &tmpType, &tmpW, &tmpH); + + GenImage3(Hobj, "byte", wid, hgt, (Hlong)hImgPtR, (Hlong)hImgPtG, (Hlong)hImgPtB); + delete[]dataR; + delete[]dataG; + delete[]dataB; + dataR = NULL; + dataG = NULL; + dataB = NULL; + } + else if (image.type() == CV_8UC1) + { + //HalconCpp::HImage himage = HalconCpp::HImage(); + + ////uchar* data = new uchar[hgt * wid]; + ////for (i = 0; i < hgt; i++) + ////{ + //// memcpy(data + wid * i, image.data + image.step * i, wid); + ////} + ////gen_image_const + //himage.GenImage1("byte", wid, hgt, image.data); + + //HalconCpp::HString tmpType; + //Hlong tmpW, tmpH; + //void* hImgPt; + //hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + //GenImage1(&Hobj, "byte", wid, hgt, (Hlong)hImgPt); + GenImage1(Hobj, "byte", wid, hgt, (Hlong)image.data); + + /* delete[] data; + data = NULL;*/ + + } + else if (image.type() == CV_16UC1) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + ushort* data = new ushort[hgt * wid * 2]; + + memcpy(data, image.data, hgt * wid * 2); + + + himage.GenImage1("uint2", wid, hgt, data); + + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPt; + hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + GenImage1(Hobj, "uint2", wid, hgt, (Hlong)hImgPt); + + delete[] data; + data = NULL; + + } + else if (image.type() == CV_16SC1) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + short* data = new short[hgt * wid * 2]; + + memcpy(data, image.data, hgt * wid * 2); + + + himage.GenImage1("int2", wid, hgt, data); + + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPt; + hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + GenImage1(Hobj, "int2", wid, hgt, (Hlong)hImgPt); + + delete[] data; + data = NULL; + + } + +} + +//判断文件是否存在,不存在则创建 +bool isFileExist(QString fullFileName) +{ + QFileInfo fileInfo(fullFileName); + if (fileInfo.isFile()) + { + return true; + } + return false; +} + +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 putTextZH(cv::Mat& dst, const char* str, cv::Point org, cv::Scalar color, int fontsize, + const char* fn = "Arial", bool italic = false, bool underline = false) +{ + CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3)); + + + int x, y, r, b; + //坐标点大于图像宽和高直接返回 + if (org.x > dst.cols || org.y > dst.rows) return; + x = org.x < 0 ? -org.x : 0; + y = org.y < 0 ? -org.y : 0; + + + LOGFONTA lf; + lf.lfHeight = -fontsize; //字体高度 + lf.lfWidth = 0; //平均宽度 + lf.lfEscapement = 0; //字符排列角度 + lf.lfOrientation = 0; //字符本身旋转的角度 + lf.lfWeight = 5; //设置字体线条的宽度 + lf.lfItalic = italic; //斜体 + lf.lfUnderline = underline; //下划线 + lf.lfStrikeOut = 0; //是否字符中央加横线 + lf.lfCharSet = DEFAULT_CHARSET; //字符集 + lf.lfOutPrecision = 0; //字体的精确度 + lf.lfQuality = PROOF_QUALITY; //字体质量 + lf.lfPitchAndFamily = 0; //选择字体的间距和字体家族 + strcpy_s(lf.lfFaceName, fn); //字体的名称 + + + HFONT hf = CreateFontIndirectA(&lf); + HDC hdc = CreateCompatibleDC(0); + HFONT holdfont = (HFONT)SelectObject(hdc, hf); + + + int strBaseW = 0, strBaseH = 0; + int singleRow = 0; + char buf[1 << 12]; + strcpy_s(buf, str); + char* bufT[1 << 12]; //这个用于分隔字符串后剩余的字符,可能会超出。 + + + //处理多行 + { + int nnh = 0; + int cw, ch; + + + const char* ln = strtok_s(buf, "\n", bufT); + while (ln != 0) + { + GetStringSize(hdc, ln, &cw, &ch); + strBaseW = max(strBaseW, cw); + strBaseH = max(strBaseH, ch); + + + ln = strtok_s(0, "\n", bufT); + nnh++; + } + singleRow = strBaseH; + strBaseH *= nnh; + } + + + if (org.x + strBaseW < 0 || org.y + strBaseH < 0) + { + SelectObject(hdc, holdfont); + DeleteObject(hf); + DeleteObject(hdc); + return; + } + + + r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1; + b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1; + org.x = org.x < 0 ? 0 : org.x; + org.y = org.y < 0 ? 0 : org.y; + + + BITMAPINFO bmp = { 0 }; + BITMAPINFOHEADER& bih = bmp.bmiHeader; + int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4)); + + + bih.biSize = sizeof(BITMAPINFOHEADER); + bih.biWidth = strBaseW; + bih.biHeight = strBaseH; + bih.biPlanes = 1; + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + bih.biSizeImage = strBaseH * strDrawLineStep; + bih.biClrUsed = 0; + bih.biClrImportant = 0; + + + void* pDibData = 0; + HBITMAP hBmp = CreateDIBSection(hdc, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0); + + + CV_Assert(pDibData != 0); + HBITMAP hOldBmp = (HBITMAP)SelectObject(hdc, hBmp); + + + //color.val[2], color.val[1], color.val[0] + SetTextColor(hdc, RGB(255, 255, 255)); + SetBkColor(hdc, 0); + //SetStretchBltMode(hDC, COLORONCOLOR); + + + strcpy_s(buf, str); + const char* ln = strtok_s(buf, "\n", bufT); + int outTextY = 0; + while (ln != 0) + { + TextOutA(hdc, 0, outTextY, ln, strlen(ln)); + outTextY += singleRow; + ln = strtok_s(0, "\n", bufT); + } + uchar* dstData = (uchar*)dst.data; + int dstStep = dst.step / sizeof(dstData[0]); + unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep; + unsigned char* pStr = (unsigned char*)pDibData + x * 3; + for (int tty = y; tty <= b; ++tty) + { + unsigned char* subImg = pImg + (tty - y) * dstStep; + unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep; + for (int ttx = x; ttx <= r; ++ttx) + { + for (int n = 0; n < dst.channels(); ++n) { + double vtxt = subStr[n] / 255.0; + int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n]; + subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv); + } + + + subStr += 3; + subImg += dst.channels(); + } + } + + + SelectObject(hdc, hOldBmp); + SelectObject(hdc, holdfont); + DeleteObject(hf); + DeleteObject(hBmp); + DeleteDC(hdc); +} + +void drawDefects(cv::Mat& sourceMat, QQueue defectInfoList) +{ + for (int i = 0; i < defectInfoList.size(); i++) + { + defectInfo mdefect = defectInfoList[i]; + + cv::rectangle(sourceMat, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y), + cv::Point(mdefect.defectRect.x + mdefect.defectRect.width, mdefect.defectRect.y + mdefect.defectRect.height), mdefect.defectScalar, 3, 8); + + QByteArray path_arr = mdefect.defectName.toLocal8Bit(); + const char* path = path_arr.data(); + putTextZH(sourceMat, path, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y - 50), mdefect.defectScalar, 50, "微软雅黑"); + } +} + + + +//判断文件夹是否存在,不存在则创建 +void Hd_AI_algo_jiZhuCeLiangMain::isDirExist(QString fullDirName) +{ + QDir* photo = new QDir; + bool exist = photo->exists(fullDirName); + if (exist) + { + //qDebug() << fullDirName << "::已存在"; + } + else + { + //创建photo文件夹 + //qDebug() << fullDirName << "::不存在,创建"; + photo->mkdir(fullDirName); + } + delete photo; + photo = nullptr; +} + +Hd_AI_algo_jiZhuCeLiangMain* create() +{ + qDebug() << "[INFO] " << " Hd_AI_algo_jiZhuCeLiangMain create success!" ; + return new Hd_AI_algo_jiZhuCeLiangMain; +} + +void destory(Hd_AI_algo_jiZhuCeLiangMain* ptr) +{ + qDebug() << "[INFO] " << "Hd_AI_algo_jiZhuCeLiangMain destory success!"; + if(ptr) + { + delete ptr; + ptr = nullptr; + } +} + +Hd_AI_algo_jiZhuCeLiangMain::Hd_AI_algo_jiZhuCeLiangMain() +{ + detect_mutex = new QMutex; + saveStruct_mutex = new QMutex; + ifdetectThread = false; + for (int i = 0; i < 4; i++) + { + QString DirPath = "E:/testImg/source" + QString::number(i); + QStringList imgFilters; + imgFilters << "*.jpg" << "*.png" << "*.PNG" << "*.gif" << "*.tif" << "*.jpeg" << "*.bmp"; + QDir qdir(DirPath); + //返回文件名列表 + QStringList localSourceImgsName = qdir.entryList(imgFilters, QDir::Files | QDir::Readable, QDir::Name); + for (int j = 0; j < localSourceImgsName.size(); j++) + { + QString imgPath = DirPath + "/" + localSourceImgsName[j]; + QFile myf(imgPath); + if (myf.exists()) + myf.remove(); + //Sleep(1); + } + } +} + +Hd_AI_algo_jiZhuCeLiangMain::~Hd_AI_algo_jiZhuCeLiangMain() +{ + ifdetectThread = false; + Sleep(10); + if (detect_mutex) + { + delete detect_mutex; + detect_mutex = nullptr; + } + if (saveStruct_mutex) + { + delete saveStruct_mutex; + saveStruct_mutex = nullptr; + } + releaseSharedMemory(); + /*DestroySegObj(pSeg); + DestroyClsObj(pCls);*/ +} +//从类中读数据到实例对象 +bool Hd_AI_algo_jiZhuCeLiangMain::readData(std::vector&vec_mat,QByteArray &data) +{ + detect_mutex->lock(); + //qDebug() << __FUNCTION__ << " line:" <<__LINE__ << " data len:" << data.length(); + if (resMat.size()>0) + { + vec_mat.push_back(resMat[0]); + resMat.clear(); + } + else + { + if(HikMat.size()>0) + vec_mat.push_back(HikMat[0]); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << " resMat.data err:"; + } + //if (allNum++ % 2 == 0) + // data = "2*3*2";// resByte.dequeue(); + //else + data = "1&" + QByteArray::number(algoResult) + "+" + QByteArray::number(algoResult);//1OK,2NG + resByte.clear(); + ////之后根据结果类修改 + //if(resIfNg == true) + // data = "NG"; + //else data = "OK"; + detect_mutex->unlock(); + /*if(cameraKey_index.contains('_')) + qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName:" + << cameraKey_index.split("_")[0] << " imgIndex:" << cameraKey_index.split("_")[1]; + else + qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName imgIndex:" << cameraKey_index;*/ + return true; +} +//从类中读数据到实例对象 +bool Hd_AI_algo_jiZhuCeLiangMain::encodeResultValue(QVector append_value,QByteArray &resultByte) +{ + try + { + std::string sendMsg; + for(int i = 0 ; i < append_value.size();i++) + { + std::string value = append_value.at(1); + //type + int type = 3; + std::string m_type; + m_type.insert(0,4,'1'); + memcpy ( (void*)m_type.c_str(),(void*)&type,4 ); + sendMsg.append(m_type); + + //len + int len = value.length(); + std::string m_len; + m_len.insert(0,4,'1'); + memcpy ( (void*)m_len.c_str(),(void*)&len,4 ); + sendMsg.append(m_len); + + //data x + sendMsg.append(value); + } + resultByte = QByteArray::fromStdString(sendMsg); + } catch (QString ev) + { + qCritical() << "[Error] " << " ev:" << ev; + return false; + } + return true; +} + +int myallNum[4] = {0}; +QMap lastImgMap; +//接收数据 +bool Hd_AI_algo_jiZhuCeLiangMain::writeData(std::vector& vec_mat, QByteArray &data) +{ + //qDebug() << __FUNCTION__ << "line:"<< __LINE__ << " data len" << data.length() ; + if(vec_mat.empty()) + { + qCritical() << "[Error] " << " vec_mat is null !"; + return false; + } + detect_mutex->lock(); + HikMat.clear(); + for (int i = 0; i < vec_mat.size(); i++) + HikMat.enqueue(vec_mat[i]); + + QByteArray source_savePath; + QByteArray sData = data; + QByteArrayList splitDataList = sData.split(','); + if (splitDataList.size() >= 4) + { + if (splitDataList[2].contains('_')) + cameraKey_index = splitDataList[2]; + QByteArray saveDir = splitDataList[0] + "/" + splitDataList[1] + "/"; + isDirExist(byteArrayToUnicode(saveDir)); //不存在,创建 + QString sdataTime= QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz"); + source_savePath = splitDataList[0] + "/" + splitDataList[1] + "/" + sdataTime.toLocal8Bit() + "-" + splitDataList[2] + "-" + splitDataList[3];// +".bmp"; + } + //int index = -1; + /*if (sData.contains("offline-camera0")) + { + index = 0; + savePath = "E:\\testImg\\source0\\"+QString::number(myallNum[0]++) + ".bmp"; + } + else if (sData.contains("offline-camera1")) + { + index = 1; + savePath = "E:\\testImg\\source1\\" + QString::number(myallNum[1]++) + ".bmp"; + } + else if (sData.contains("offline-camera2")) + { + index = 2; + savePath = "E:\\testImg\\source2\\" + QString::number(myallNum[2]++) + ".bmp"; + } + else if (sData.contains("offline-camera3")) + { + index = 3; + savePath = "E:\\testImg\\source3\\" + QString::number(myallNum[3]++) + ".bmp"; + }*/ + + + createDirectoryIfNotExists(oriImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//"); + addImgToSaveStruct(HikMat[0], "0", geteOriSaveFromat());//保存原图 + //if (!source_savePath.isEmpty()) + //{ + // //std::cout << "try savePath::" << savePath.toStdString() << std::endl; + // //测试相机的, + // //while (HikMat[0].cols > 2000)cv::resize(HikMat[0], HikMat[0], cv::Size(), 0.08, 0.08);' + // for (int i = 0; i < initValueList.size(); i++) + // { + // QString minit = byteArrayToUnicode(initValueList[i]); + // if (initValueList[i].contains(data.split(',')[2].split('_')[0])) + // if (initValueList[i].contains(QString("原图_保存").toLocal8Bit())) + // { + // addImgToSaveStruct(HikMat[0], "0", byteArrayToUnicode(source_savePath + ".bmp"));//保存原图 + // if (HikMat.size() == 2)//3D + // addImgToSaveStruct(HikMat[1], "0", byteArrayToUnicode(source_savePath + ".tif")); + // } + // } + //} + + //if (myallNum[index] == 1) + //{ + // cv::Mat img; + // lastImgMap.insert(index, img); + //} + //else + //{ + // //判断和上一张图,是否重复 + // int dataLength = HikMat[0].total() * HikMat[0].elemSize(); + // bool res = false; + // if (myallNum[index] > 0) + // { + // cv::Mat img = lastImgMap.value(index); + // if (img.total() * img.elemSize() == dataLength) + // { + // res = memcmp(img.data, HikMat[0].data, dataLength) == 0; + // if (res == true) + // std::cout << "res ==true ====!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + // } + // } + //} + //lastImgMap.insert(index, HikMat[0]); + + //or检测:保存路径,拍照面描述,相机key_第几张图,SN_时间戳 + //and检测:保存路径,拍照面描述,相机key_第几张图,相机key_第几张图,SN_时间戳,SN_时间戳 + //汇总:保存路径,拍照面描述,触发信号_序号列表0,SN_时间戳 + QString qstring_data = data; + QStringList datalist = qstring_data.split(','); + + + /*saveKey.enqueue("datalist[0]"); + Dir2D.enqueue("datalist[1]"); + QStringList SN_dataTime; + SN_dataTime = datalist[3].split('_'); + DateTime1 = "SN_dataTime[1]"; + SN.enqueue("SN_dataTime[0]");*/ + //qCritical() << __FUNCTION__ << " line: " << __LINE__ << " data:" << qstring_data; + detect_mutex->unlock(); + + //qDebug() << __FUNCTION__ << "line:" << __LINE__ << "[INFO] " << " data: " << data << " run end"; + //cv::Mat resultDstImg = vec_mat[0].clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + //resultDstImg = pSeg->Evaluate(resultDstImg); + //resultDstImg.release(); + + return true; +} + +//传入initParas函数,格式为:相机key+参数名1#参数值1#参数值2+参数名2#参数值1... +//初始化 +bool Hd_AI_algo_jiZhuCeLiangMain::initParas(QByteArray& byte) +{ + initValueList = byte.split('+'); + QStringList valueiList; + + if (byte.contains("onlySetParameter")) + { + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,valueList[0]:" << initValueList[0]; + if (initValueList.size() > 1) + for (int i = 1; i < initValueList.size(); i++) + { + QString valuei = byteArrayToUnicode(initValueList[i]); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,lastName:" << initValueList[0] << " ,valuei:" << valuei; + } + return true; + } + + + if (initValueList.size() > 1) + for (int i = 1; i < initValueList.size(); i++) + { + QString valuei = byteArrayToUnicode(initValueList[i]); + valueiList.append(valuei); + } + + /*pSeg = CreateSegObjS(pSeg_modulePath, 128, gpuId); + pCls = CreateClsObjV3(pCls_modulePath, gpuId, 1);*/ + + + //StationNo = vec_ret.at(0).toInt(); + //InitThreads(StationNo, img2D); + //InitModels(StationNo, img2D); + + //运行检测线程 + ifdetectThread = true; + + //线程检测:结果类完成时,使用 + //ifThreadRunDetect = true; + //QFuture future1 = QtConcurrent::run(this, &Hd_AI_algo_jiZhuCeLiangMain::detectThread); + //运行存图线程 + QFuture future2 = QtConcurrent::run(this, &Hd_AI_algo_jiZhuCeLiangMain::saveImgThread); + //detectThread(); + initSharedMemory(); + //原图总文件夹 + isDirExist(oriImgSaveDir); + //原图class总文件夹 + isDirExist(classImgSaveDir); + //结果图总文件夹 + isDirExist(resImgSaveDir); + + //qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ; + return true; +} +// 解码 +bool Hd_AI_algo_jiZhuCeLiangMain::decodeAiValue(QByteArray byte,QVector &vec_ret) +{ + if(byte.isEmpty()) + { + qCritical() << "[Error] " << " byte is null!"; + return false; + } + + /*******************************************/ + byte.remove(0,8); + QByteArray mm_data = byte.mid(0,4); + memcpy((void*)¶mNum,(void*)mm_data.data(), 4); + qDebug() << "[INFO] " << " paramNum:" << paramNum; + byte.remove(0,4); + /*******************************************/ + + while (!byte.isEmpty()) + { + int type = 0; + QByteArray m_type = byte.mid(0,4); + memcpy((void*)&type,(void*)m_type.data(), 4); + byte.remove(0,4); + + int len = 0; + QByteArray m_len = byte.mid(0,4); + memcpy((void*)&len,(void*)m_len.data(), 4); + byte.remove(0,4); + + QByteArray mm_data = byte.mid(0,len); + vec_ret.append(mm_data); + byte.remove(0,len); + } + return true; +} +//---------检测流程,根据实际项目修改--------- +bool Hd_AI_algo_jiZhuCeLiangMain::AIdetect(cv::Mat img2D, QString mysaveKey, QString myDir2D, QString mySN, QString myDataTime) +{ + try + { + //存图数据加保存队列,加锁,避免数据访问冲突 + saveStruct_mutex->lock(); + saveImgStruct mySave; + Mat sourceImg; + sourceImg = img2D.clone(); + //img2D.copyTo(sourceImg); + //img2D.release(); + QString mysaveKey1 = mysaveKey; //"offlineStation1_off_1" + saveStruct_mutex->unlock(); + + QJsonDocument document; + QJsonObject obj; + obj.insert("result_key", "mysaveKey"); + obj.insert("SN", "mySN"); + obj.insert("DataTime", "myDataTime"); + + + //QFile csvName("test.csv"); + //QStringList lines; + //lines << "class,name\n" << "3,4,5,6,7\n" << "a,b,c,d\n"; + //if (csvName.open(QIODevice::WriteOnly)) //若文件不存在,会自动创建 + //{ + // for (int i = 0; i < lines.size(); i++) + // csvName.write(lines[i].toStdString().c_str()); + // csvName.close(); + //} + //sourceImg = cv::imread("D:\\mySN15-46-33-86.bmp",-1); + //sourceImg = cv::imread("D:/1/2820698.bmp", -1); + sourceImg = cv::imread("D:/1/ruanLianJie/fj/2.bmp",-1); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if(Paint.channels() == 1) + cvtColor(Paint, Paint, COLOR_GRAY2BGR); + + HObject ho_Image_L; + HObject ho_Image_T; + HTuple hv_Pix_L(0.02); + HTuple hv_Pix_T(0.02); + HTuple hv_Out_Line_T; + HTuple hv_Out_Point_T; + HTuple hv_Out_Chamfering_T; + HTuple hv_Distances_T; + HTuple hv_Out_Line_L; + HTuple hv_Out_Point_L; + HTuple hv_Out_Chamfering_L; + HTuple hv_Distances_L; + HTuple hv_Out_InterpoleValue; + HTuple hv_Makes; + MatToHObject(sourceImg, &ho_Image_L); + while (g_unionCalculate->getData(ho_Image_T) == false) + QThread::msleep(1); + _628_3_PoleDetections(ho_Image_L, ho_Image_T, + hv_Pix_L,hv_Pix_T, + &hv_Out_Line_T, &hv_Out_Point_T, + &hv_Out_Chamfering_T,&hv_Distances_T, + &hv_Out_Line_L,& hv_Out_Point_L, + &hv_Out_Chamfering_L,&hv_Distances_L, + &hv_Out_InterpoleValue,&hv_Makes); + if(hv_Makes.D() == 1) + { + double interpoleValue = hv_Out_InterpoleValue[0].D(); + + int hv_Out_Line_T_Length = hv_Out_Line_T.Length(); + int hv_Out_Point_T_Length = hv_Out_Point_T.Length(); + double edgeValue_T = hv_Out_Chamfering_T[0].D(); + double upDownDistance_T = hv_Distances_T[0].D() + , leftRightDistance_T = hv_Distances_T[1].D(); + + int hv_Out_Line_L_Length = hv_Out_Line_L.Length(); + int hv_Out_Point_L_Length = hv_Out_Point_L.Length(); + double edgeValue_L = hv_Out_Chamfering_L[0].D(); + double upDownDistance_L = hv_Distances_L[0].D() + , leftRightDistance_L = hv_Distances_L[1].D(); + + //发送到另一算法线程 + unionCalculate::calResult _calResult; + _calResult.upDownDistance = upDownDistance_L; + _calResult.leftRightDistance = leftRightDistance_L; + _calResult.interpoleValue = interpoleValue; + for (int i = 0; i < hv_Out_Line_L_Length; i++) + _calResult.lines.push_back(hv_Out_Line_L[i].D()); + for (int i = 0; i < hv_Out_Point_L_Length; i++) + _calResult.points.push_back(hv_Out_Point_L[i].D()); + g_unionCalculate->insertResult(_calResult); + + //Paint + { + std::string text = "垂直间距:" + std::to_string(upDownDistance_T); + text = text.substr(0, text.find(".") + 4) + "mm"; + putTextHusky(Paint, + utf8_to_gbk(text).c_str(), + cv::Point(50, 50), cv::Scalar(0, 0, 255), 280); + } + { + std::string text = "水平间距:" + std::to_string(leftRightDistance_T); + text = text.substr(0, text.find(".") + 4) + "mm"; + putTextHusky(Paint, + utf8_to_gbk(text).c_str(), + cv::Point(50, 300), cv::Scalar(0, 0, 255), 280); + } + { + std::string text = " 极间距:" + std::to_string(interpoleValue); + 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); + } + for (int i = 0; i < hv_Out_Line_T_Length / 4; i++) + cv::line(Paint, cv::Point(hv_Out_Line_T[i * 4 + 1].D(), hv_Out_Line_T[i * 4].D()), + cv::Point(hv_Out_Line_T[i * 4 + 3].D(), hv_Out_Line_T[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 20); + for (int i = 0; i < hv_Out_Point_T_Length / 3; i++) + cv::circle(Paint, cv::Point(hv_Out_Point_T[i * 3 + 1].D(), hv_Out_Point_T[i * 3].D()), + hv_Out_Point_T[i * 3 + 2].D(), + cv::Scalar(0, 255, 0), 20); + + } + else + { + putTextHusky(Paint, byteArrayToUnicode("算法错误").toStdString().c_str(), cv::Point(0, 50), cv::Scalar(0, 255, 0), 500); + unionCalculate::calResult _calResult; + _calResult.algoFailed = true; + g_unionCalculate->insertResult(_calResult); + } + + + + + + detect_mutex->lock(); + + + + + QQueue defectInfoList; + resMat.push_back(Paint); + QJsonArray detaillists; + //QByteArrayList resQbyte; + for (int i = 0; i < defectInfoList.size(); i++) + { + defectInfo mdefect = defectInfoList[i]; + //resQbyte.push_back(mdefect.defectName.toLocal8Bit()); //中文也可以QString::number(1.1).toUtf8() + //resQbyte.push_back(QString::number(mdefect.defectRect.x).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.y).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.width).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.height).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[0]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[1]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[2]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectArea).toUtf8()); + QJsonObject detailObj; + detailObj.insert("defectName", "mdefect.defectName"); + detailObj.insert("defectRect.x", QString::number(mdefect.defectRect.x)); + detailObj.insert("defectRect.y", QString::number(mdefect.defectRect.y)); + detailObj.insert("defectRect.width", QString::number(mdefect.defectRect.width)); + detailObj.insert("defectRect.height", QString::number(mdefect.defectRect.height)); + detailObj.insert("defectScalar0", QString::number(mdefect.defectScalar[0])); + detailObj.insert("defectScalar1", QString::number(mdefect.defectScalar[1])); + detailObj.insert("defectScalar2", QString::number(mdefect.defectScalar[2])); + detailObj.insert("defectArea", QString::number(mdefect.defectArea)); + detailObj.insert("defectLength", QString::number(sqrt(mdefect.defectRect.width* mdefect.defectRect.width+ mdefect.defectRect.height* mdefect.defectRect.height))); + + detaillists.append(detailObj); + } + + + //使用完删除 + defectInfoList.clear(); + + //往主界面发 + //emit sendResultData(resMat, resQbyte); + //resQbyte.clear(); + + //isDirExist(resImgSaveDir + mysaveKey1 + "/"); + QString qimgsavePath = resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png"; + //存图数据入队,按需 + if (resMat.size() > 0 ) + { + createDirectoryIfNotExists(resImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//"); + addImgToSaveStruct(resMat[0], "0", getResSaveFromat());//保存原图 + } + detect_mutex->unlock(); + + obj.insert("detaillists", detaillists); + obj.insert("qimgsavePath", qimgsavePath); + document.setObject(obj); + //resByte.clear(); + QByteArray qa = document.toJson(); + //resByte .enqueue(qa); + + + //resMat.release(); + sourceImg.release(); + + mySave.out_Mat.resize(0); + mysaveKey1.clear(); + qApp->processEvents(); + + + ////原图和结果都往主界面发? + //std::vector resMat; + //resMat.push_back(img2D); + //resMat.push_back(sourceRectangles); + //QByteArray resByte = mysaveKey.toLocal8Bit(); + //resByte.append(myDir2D.toLocal8Bit()); + //resByte.append(mySN.toLocal8Bit()); + //resByte.append(myDataTime.toLocal8Bit()); + + ////往主界面发送图片和数据 + ////emit writeData(resMat, resByte); + //resMat.resize(0); + //resByte.resize(0); + } + catch (...) + { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + return false; + } + return true; + +} + +//存图加入队列 +void Hd_AI_algo_jiZhuCeLiangMain::addImgToSaveStruct(cv::Mat img2D, QString mysaveKey, QString savePath) +{ + //存图数据加保存队列,加锁,避免数据访问冲突 + saveStruct_mutex->lock(); + saveImgStruct mySave; + mySave.out_Mat = img2D.clone(); + //img2D.copyTo(mySave.out_Mat); + mySave.saveKey = mysaveKey.toLocal8Bit().toStdString(); + mySave.savePath = savePath.toLocal8Bit().toStdString(); + saveStruct.enqueue(mySave); + saveStruct_mutex->unlock(); +} +void Hd_AI_algo_jiZhuCeLiangMain::saveImgThread() +{ + while (1) + { + if (!ifdetectThread)return; + _sleep(5); + qApp->processEvents(); + if (saveStruct.size() < 1)continue; + for (int i = 0; i < saveStruct.size(); i++) + { + clock_t start, stop; + start = clock(); + saveStruct_mutex->lock(); + saveImgStruct mysaveStruct = saveStruct.dequeue(); + saveStruct_mutex->unlock(); + stop = clock(); + if(stop-start >1000) + std::cout << "lock stop-start >1000::************************" << stop - start << std::endl; + start = clock(); + if (mysaveStruct.out_Mat.data) + imwrite(mysaveStruct.savePath, mysaveStruct.out_Mat); + //std::cout << "savePath::" << mysaveStruct.savePath << std::endl; + stop = clock(); + if (stop - start > 1000) + std::cout << "imwrite stop-start >1000::--------------------------"<< stop - start << std::endl; + _sleep(1); + } + } +} +//检测线程,应当只运行一次 +void Hd_AI_algo_jiZhuCeLiangMain::detectThread() +{ + do + { + if (!ifdetectThread)return; + + //此两行代码,避免程序卡死;_sleep的时间根据实际项目调整 + _sleep(2); + qApp->processEvents(); + //取数据加锁,避免数据访问冲突 + detect_mutex->lock(); + //if (HikMat.size() < 1 || saveKey.size() < 1 || Dir2D.size() < 1 || SN.size() < 1 )//|| DateTime1.size() < 1) + //{ + // detect_mutex->unlock(); + // continue; + //} + + cv::Mat sourceImg1 = HikMat[0];// .dequeue(); + QString mysaveKey = "saveKey";// .dequeue(); + QString myDir2D = "Dir2D";// .dequeue(); + QString mySN = "SN";// .dequeue(); + QString myDataTime = "DateTime1"; + detect_mutex->unlock(); + + //判断存图文件夹是否存在,不存在则创建 + { + //原图总文件夹 + isDirExist(oriImgSaveDir + mysaveKey); + //原图class总文件夹 + isDirExist(classImgSaveDir + mysaveKey); + //结果图总文件夹 + isDirExist(resImgSaveDir + mysaveKey); + + } + + //测试相机的, + // while (sourceImg1.cols > 2000)cv::resize(sourceImg1, sourceImg1, cv::Size(), 0.5, 0.5); + + //存图数据入队 + //addImgToSaveStruct(sourceImg1, mysaveKey, oriImgSaveDir + mysaveKey + "/" + mySN + myDataTime + ".bmp"); + + //-------检测流程,根据实际项目修改--------- + AIdetect(sourceImg1, mysaveKey, oriImgSaveDir, mySN, myDataTime); + //sourceImg1.release(); + + mysaveKey.clear(); + myDir2D.clear(); + mySN.clear(); + myDataTime.clear(); + qApp->processEvents(); + + } while (ifThreadRunDetect); + +} + +static int runNum = 0; +bool Hd_AI_algo_jiZhuCeLiangMain::run() +{ + clock_t start, stop; + start = clock(); + + //Sleep(1000); + detectThread(); + stop = clock(); + qDebug() << __FUNCTION__ << "line:" << __LINE__ << " run start ::runNum::" << runNum++<<" cutTime::"< +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "imgproc/imgproc_c.h" +#include "base.h" +#include "handleAI.h" +#include +#pragma execution_character_set("utf-8") + +//保存图片的结构体 +struct saveImgStruct; +//{ +// cv::Mat out_Mat; +// std::string saveKey; //关键字,用于LOG日志 +// std::string savePath; //保存路径 +//}; + +//初始化流程(一次):Hd_AI_algo_jiZhuCeLiangMain,initParas +//运行流程:writeData主线程把数据传过来,run,readData此线程把数据传主线程 +class Hd_AI_algo_jiZhuCeLiangMain :public Base +{ + Q_OBJECT +public: + Hd_AI_algo_jiZhuCeLiangMain(); + virtual ~Hd_AI_algo_jiZhuCeLiangMain(); + //从类中读数据到实例对象 + bool readData(std::vector&,QByteArray&); + //实例对象把数据写入到类 + bool writeData(std::vector&, QByteArray&); + bool initParas(QByteArray&); + //运行(Logic_taskthread.cpp) + bool run(); + //线程状态 + bool checkStatus();// { return true; }; + + bool encodeResultValue(QVector append_value,QByteArray &resultByte); + bool decodeAiValue(QByteArray byte,QVector &vec_ret); + //---------检测流程,根据实际项目修改--------- + bool AIdetect(cv::Mat img2D, QString saveKey, QString Dir2D, QString SN, QString DataTime); + +private: + int allNum = 0; + int StationNo=0; + int img2D = 2; + + bool moduleStatus = true; + + //输入图片队列 + QQueue HikMat; + //输入关键字队列 + QString saveKey="saveKey"; + //结果路径文件夹队列 + QString Dir2D="Dir2D"; + //产品SN队列 + QString SN="SN"; + //产品时间戳队列 + QString DateTime1="DateTime1"; + + int paramNum; + + //是否使用线程检测方式 + bool ifThreadRunDetect = false; + //检测结果图 + std::vector resMat; + //检测结果 + bool resIfNg = false; + + QQueue resByte; + + //检测线程 + void detectThread(); + //存图线程 + void saveImgThread(); + + //保存图片队列 + QQueue saveStruct; + + //判断文件夹是否存在,不存在则创建 + void isDirExist(QString fullDirName); + + //存图加入队列 + void addImgToSaveStruct(cv::Mat img2D, QString saveKey, QString savePath); +}; + +extern "C" +{ + Q_DECL_EXPORT Hd_AI_algo_jiZhuCeLiangMain * create(); + Q_DECL_EXPORT void destory(Hd_AI_algo_jiZhuCeLiangMain * ptr); +} +//保存图片的结构体 +struct saveImgStruct +{ + cv::Mat out_Mat; + std::string saveKey; //关键字,用于LOG日志 + std::string savePath; //保存路径 +}; +#endif // Hd_AI_927_2D_module_H diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/base.h b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/base.h new file mode 100644 index 0000000..00bd6b2 --- /dev/null +++ b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/base.h @@ -0,0 +1,69 @@ +#ifndef __BASECLASS_H__ +#define __BASECLASS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Base :public QObject +{ + Q_OBJECT +public: + bool init_flag; //是否初始化,false,true + bool status = true; //相机状态(判断相机是否断开连接) + std::string paras; //传输数据 + std::vector m_mat; //传输图片 + std::string moduleName; //模块名称 +public: + Base(){} + virtual ~Base(){} + virtual bool readData(std::vector&,QByteArray&){return true;} + virtual bool writeData(std::vector&,QByteArray&){return true;} + virtual bool initParas(QByteArray&){return true;} + virtual bool run() {return true;} + virtual bool checkStatus() { return true; } + +//signals: + //QByteArrayList,根据实际项目进行解析;因不确定每个参数的长度,固用队列的方式。每个QByteArray表示一个值,使用时先转换为QString + //参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double) + //void sendResultData(cv::Mat resMat, QJsonObject resQbj); +}; + +/* +//QJsonObject 转QByteArray +{ + QJsonDocument document; + QJsonObject obj; + obj.insert(password, "pwd"); + obj.insert(userName, "usr"); + document.setObject(obj); + QByteArray abyte = document.toJson(QJsonDocument::Compact); +} + +//QByteArray转QJsonObject +{ + QByteArray ba = reply->readAll(); + QJsonDocument doc = QJsonDocument::fromJson(ba); + QJsonObject obj = doc.object(); +} +*/ +/* +//从类中读数据到实例对象 +virtual bool readData(std::vector&,QByteArray&){return true;} + + + + + + + + +*/ + +#endif diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/sharedMem_Main.h b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/sharedMem_Main.h new file mode 100644 index 0000000..6850c39 --- /dev/null +++ b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/sharedMem_Main.h @@ -0,0 +1,59 @@ +#include +#include +#include "unionCal.h" +// 全局变量,用于指向 unionCalculate 实例的指针 +unionCalculate* g_unionCalculate = nullptr; +HANDLE hMapFile; +unionCalculate** sharedUnionCalculatePtr = nullptr; + +// 创建或获取共享内存,用于存储指向 unionCalculate 类实例的指针 +extern "C" __declspec(dllexport) void initSharedMemory() { + // 创建共享内存对象,存储 unionCalculate* 指针 + hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(unionCalculate*), "unionCalculatePtr"); + + if (hMapFile == NULL) { + std::cerr << "Could not create file mapping object (" << GetLastError() << ")" << std::endl; + return; + } + + // 将共享内存映射到当前进程的地址空间 + sharedUnionCalculatePtr = (unionCalculate**)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(unionCalculate*)); + + if (sharedUnionCalculatePtr == NULL) { + std::cerr << "Could not map view of file (" << GetLastError() << ")" << std::endl; + CloseHandle(hMapFile); + return; + } + + // 如果指针为空,创建一个新的 unionCalculate 实例并保存 + if (*sharedUnionCalculatePtr == nullptr) { + g_unionCalculate = new unionCalculate(); + *sharedUnionCalculatePtr = g_unionCalculate; // 存储指针到共享内存 + std::cout << "Created new unionCalculate instance." << std::endl; + } + else { + // 如果已经存在,直接使用 + g_unionCalculate = *sharedUnionCalculatePtr; + std::cout << "Using existing unionCalculate instance." << std::endl; + } +} + +// 释放共享内存 +extern "C" __declspec(dllexport) void releaseSharedMemory() { + if (g_unionCalculate) { + delete g_unionCalculate; + } + + if (sharedUnionCalculatePtr) { + UnmapViewOfFile(sharedUnionCalculatePtr); + } + + if (hMapFile) { + CloseHandle(hMapFile); + } +} + +// 获取指向 unionCalculate 实例的指针 +extern "C" __declspec(dllexport) unionCalculate* getUnionCalculateInstance() { + return g_unionCalculate; +} \ No newline at end of file diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/铝极_要比铜极先初始化 b/BaseModules/Hd_AI_algo_jiZhuCeLiangMain/铝极_要比铜极先初始化 new file mode 100644 index 0000000..e69de29 diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/CMakeLists.txt b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/CMakeLists.txt new file mode 100644 index 0000000..be2265d --- /dev/null +++ b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/CMakeLists.txt @@ -0,0 +1,71 @@ +# 获取获取当前目录名 +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" "halconAlgo" "myTools" "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( + + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) + + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC ON) +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 REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS network REQUIRED) +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES LINKER_LANGUAGE C + 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}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " model end : " ${CURRENT_FOLDER}:${RUN_TIME}) diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/Hd_AI_algo_jiZhuCeLiangSub.cpp b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/Hd_AI_algo_jiZhuCeLiangSub.cpp new file mode 100644 index 0000000..80d5425 --- /dev/null +++ b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/Hd_AI_algo_jiZhuCeLiangSub.cpp @@ -0,0 +1,1087 @@ +#include "Hd_AI_algo_jiZhuCeLiangSub.h" +#include + +#include +#include +#include +#include +#include +#include +#include "sharedMen_Sub.h" +using namespace cv; +//原图保存文件夹s + QString oriImgSaveDir = "D:/601线/软连接CCD/极柱测量CCD/原图/"; +//原图class图保存文件夹 + QString classImgSaveDir = "D:/601线/软连接CCD/极柱测量CCD/class图/"; +//结果保存文件夹 + QString resImgSaveDir = "D:/601线/软连接CCD/极柱测量CCD/结果图/"; + int algoResult = 0;//0 OK 1 NG +//切图的class的width,根据项目实际设置! + int sizeW = 150; +//切图的class的height,根据项目实际设置! + int sizeH = 150; +//切图的class的width,根据项目实际设置!0为黑,255为白; + int fillColour = 0; + + +//检测线程锁 + QMutex* detect_mutex; +//保存线程锁 + QMutex* saveStruct_mutex; +//检测线程运行标志 + bool ifdetectThread = false; + + QString cameraKey_index = ""; + + //初始化参数列表 + QByteArrayList initValueList; + + static void createDirectoryIfNotExists(const QString& path) { + QDir dir; + dir.mkpath(path); + } + static std::string getCurrentTime() { + + // 获取当前时间点 + auto now = std::chrono::system_clock::now(); + + // 转换为字符串 + auto currentTime = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 100; + std::tm* localTime = std::localtime(¤tTime); + std::stringstream ss; + ss << std::put_time(localTime, "%H-%M-%S") << "-" << std::setfill('0') << std::setw(2) << ms.count(); + return ss.str(); + } + static QString getCurrentDate() + { + QDateTime currentDateTime = QDateTime::currentDateTime(); + return currentDateTime.toString("yyyy-MM-dd"); + } + static QString getCurrentHour() + { + QDateTime currentDateTime = QDateTime::currentDateTime(); + return QString::number(currentDateTime.time().hour())+"H"; + } + QString byteArrayToUnicode(const QByteArray array) { + + // state用于保存转换状态,它的成员invalidChars,可用来判断是否转换成功 + // 如果转换成功,则值为0,如果值大于0,则说明转换失败 + QTextCodec::ConverterState state; + // 先尝试使用utf-8的方式把QByteArray转换成QString + QString text = QTextCodec::codecForName("UTF-8")->toUnicode(array.constData(), array.size(), &state); + // 如果转换时无效字符数量大于0,说明编码格式不对 + if (state.invalidChars > 0) + { + // 再尝试使用GBK的方式进行转换,一般就能转换正确(当然也可能是其它格式,但比较少见了) + text = QTextCodec::codecForName("GBK")->toUnicode(array); + } + return text; + } + static cv::Rect findMaxAreaContour(cv::Mat src) + { + int maxIndex = 0, maxArea = 0; + std::vector>contours; + cv::findContours(src, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + if (contours.size() == 0) + return cv::Rect(0, 0, 0, 0); + for (int i = 0; i < contours.size(); i++) + { + int Area = cv::contourArea(contours[i]); + if (Area > maxArea) + { + maxArea = Area; + maxIndex = i; + } + } + if (contours.size() != 0) + return cv::boundingRect(contours[maxIndex]); + return cv::Rect(0, 0, 0, 0); + } + inline std::string utf8_to_gbk(const std::string& str) + { + std::wstring_convert> conv; + std::wstring tmp_wstr = conv.from_bytes(str); + //GBK locale name in windows + const char* GBK_LOCALE_NAME = ".936"; + std::wstring_convert> convert(new std::codecvt_byname(GBK_LOCALE_NAME)); + return convert.to_bytes(tmp_wstr); + } + void getAllFiles(std::string path, std::vector& files, std::string format) + { + intptr_t hFile = 0;//文件句柄 + struct _finddata_t fileinfo;//文件信息 + std::string p; + if ((hFile = _findfirst(p.assign(path).append("\\*" + format).c_str(), &fileinfo)) != -1) //文件存在 + { + do + { + if ((fileinfo.attrib & _A_SUBDIR))//判断是否为文件夹 + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)//文件夹名中不含"."和".." + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name)); //保存文件夹名 + getAllFiles(p.assign(path).append("\\").append(fileinfo.name), files, format); //递归遍历文件夹 + } + } + else + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name));//如果不是文件夹,储存文件名 + } + } while (_findnext(hFile, &fileinfo) == 0); + _findclose(hFile); + } + } + QString geteOriSaveFromat() + { + return oriImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//" + + "ORI_" + cameraKey_index + "_" + QString::fromStdString(getCurrentTime()) + ".bmp"; + } + QString getResSaveFromat() + { + return resImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//" + + "RES_" + cameraKey_index + "_" + QString::fromStdString(getCurrentTime()) + ".png"; + } +// 缺陷信息结构体 +typedef struct { + QString defectName = "";//缺陷名称 + cv::Rect defectRect = { 0,0,0,0 };//缺陷位置 + cv::Scalar defectScalar = cv::Scalar(0, 0, 0);//画框颜色 + double defectArea = 0;//缺陷真实面积 + double defectDepth = 0;//缺陷深度/高度,3D信息 +}defectInfo; + +//Mat转Halcon图像 +void MatToHObject(cv::Mat& image, HalconCpp::HObject* Hobj) +{ + int hgt = image.rows; + int wid = image.cols; + int i; + if (image.type() == CV_8UC3) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + std::vector imgchannel; + split(image, imgchannel); + cv::Mat imgB = imgchannel[0]; + cv::Mat imgG = imgchannel[1]; + cv::Mat imgR = imgchannel[2]; + uchar* dataR = new uchar[hgt * wid]; + uchar* dataG = new uchar[hgt * wid]; + uchar* dataB = new uchar[hgt * wid]; + for (i = 0; i < hgt; i++) + { + memcpy(dataR + wid * i, imgR.data + imgR.step * i, wid); + memcpy(dataG + wid * i, imgG.data + imgG.step * i, wid); + memcpy(dataB + wid * i, imgB.data + imgB.step * i, wid); + } + himage.GenImage3("byte", wid, hgt, dataR, dataG, dataB); + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPtR, * hImgPtG, * hImgPtB; + himage.GetImagePointer3(&hImgPtR, &hImgPtG, &hImgPtB, &tmpType, &tmpW, &tmpH); + + GenImage3(Hobj, "byte", wid, hgt, (Hlong)hImgPtR, (Hlong)hImgPtG, (Hlong)hImgPtB); + delete[]dataR; + delete[]dataG; + delete[]dataB; + dataR = NULL; + dataG = NULL; + dataB = NULL; + } + else if (image.type() == CV_8UC1) + { + //HalconCpp::HImage himage = HalconCpp::HImage(); + + ////uchar* data = new uchar[hgt * wid]; + ////for (i = 0; i < hgt; i++) + ////{ + //// memcpy(data + wid * i, image.data + image.step * i, wid); + ////} + ////gen_image_const + //himage.GenImage1("byte", wid, hgt, image.data); + + //HalconCpp::HString tmpType; + //Hlong tmpW, tmpH; + //void* hImgPt; + //hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + //GenImage1(&Hobj, "byte", wid, hgt, (Hlong)hImgPt); + GenImage1(Hobj, "byte", wid, hgt, (Hlong)image.data); + + /* delete[] data; + data = NULL;*/ + + } + else if (image.type() == CV_16UC1) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + ushort* data = new ushort[hgt * wid * 2]; + + memcpy(data, image.data, hgt * wid * 2); + + + himage.GenImage1("uint2", wid, hgt, data); + + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPt; + hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + GenImage1(Hobj, "uint2", wid, hgt, (Hlong)hImgPt); + + delete[] data; + data = NULL; + + } + else if (image.type() == CV_16SC1) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + short* data = new short[hgt * wid * 2]; + + memcpy(data, image.data, hgt * wid * 2); + + + himage.GenImage1("int2", wid, hgt, data); + + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPt; + hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + GenImage1(Hobj, "int2", wid, hgt, (Hlong)hImgPt); + + delete[] data; + data = NULL; + + } + +} + +//判断文件是否存在,不存在则创建 +bool isFileExist(QString fullFileName) +{ + QFileInfo fileInfo(fullFileName); + if (fileInfo.isFile()) + { + return true; + } + return false; +} + +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 putTextZH(cv::Mat& dst, const char* str, cv::Point org, cv::Scalar color, int fontsize, + const char* fn = "Arial", bool italic = false, bool underline = false) +{ + CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3)); + + + int x, y, r, b; + //坐标点大于图像宽和高直接返回 + if (org.x > dst.cols || org.y > dst.rows) return; + x = org.x < 0 ? -org.x : 0; + y = org.y < 0 ? -org.y : 0; + + + LOGFONTA lf; + lf.lfHeight = -fontsize; //字体高度 + lf.lfWidth = 0; //平均宽度 + lf.lfEscapement = 0; //字符排列角度 + lf.lfOrientation = 0; //字符本身旋转的角度 + lf.lfWeight = 5; //设置字体线条的宽度 + lf.lfItalic = italic; //斜体 + lf.lfUnderline = underline; //下划线 + lf.lfStrikeOut = 0; //是否字符中央加横线 + lf.lfCharSet = DEFAULT_CHARSET; //字符集 + lf.lfOutPrecision = 0; //字体的精确度 + lf.lfQuality = PROOF_QUALITY; //字体质量 + lf.lfPitchAndFamily = 0; //选择字体的间距和字体家族 + strcpy_s(lf.lfFaceName, fn); //字体的名称 + + + HFONT hf = CreateFontIndirectA(&lf); + HDC hdc = CreateCompatibleDC(0); + HFONT holdfont = (HFONT)SelectObject(hdc, hf); + + + int strBaseW = 0, strBaseH = 0; + int singleRow = 0; + char buf[1 << 12]; + strcpy_s(buf, str); + char* bufT[1 << 12]; //这个用于分隔字符串后剩余的字符,可能会超出。 + + + //处理多行 + { + int nnh = 0; + int cw, ch; + + + const char* ln = strtok_s(buf, "\n", bufT); + while (ln != 0) + { + GetStringSize(hdc, ln, &cw, &ch); + strBaseW = max(strBaseW, cw); + strBaseH = max(strBaseH, ch); + + + ln = strtok_s(0, "\n", bufT); + nnh++; + } + singleRow = strBaseH; + strBaseH *= nnh; + } + + + if (org.x + strBaseW < 0 || org.y + strBaseH < 0) + { + SelectObject(hdc, holdfont); + DeleteObject(hf); + DeleteObject(hdc); + return; + } + + + r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1; + b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1; + org.x = org.x < 0 ? 0 : org.x; + org.y = org.y < 0 ? 0 : org.y; + + + BITMAPINFO bmp = { 0 }; + BITMAPINFOHEADER& bih = bmp.bmiHeader; + int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4)); + + + bih.biSize = sizeof(BITMAPINFOHEADER); + bih.biWidth = strBaseW; + bih.biHeight = strBaseH; + bih.biPlanes = 1; + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + bih.biSizeImage = strBaseH * strDrawLineStep; + bih.biClrUsed = 0; + bih.biClrImportant = 0; + + + void* pDibData = 0; + HBITMAP hBmp = CreateDIBSection(hdc, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0); + + + CV_Assert(pDibData != 0); + HBITMAP hOldBmp = (HBITMAP)SelectObject(hdc, hBmp); + + + //color.val[2], color.val[1], color.val[0] + SetTextColor(hdc, RGB(255, 255, 255)); + SetBkColor(hdc, 0); + //SetStretchBltMode(hDC, COLORONCOLOR); + + + strcpy_s(buf, str); + const char* ln = strtok_s(buf, "\n", bufT); + int outTextY = 0; + while (ln != 0) + { + TextOutA(hdc, 0, outTextY, ln, strlen(ln)); + outTextY += singleRow; + ln = strtok_s(0, "\n", bufT); + } + uchar* dstData = (uchar*)dst.data; + int dstStep = dst.step / sizeof(dstData[0]); + unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep; + unsigned char* pStr = (unsigned char*)pDibData + x * 3; + for (int tty = y; tty <= b; ++tty) + { + unsigned char* subImg = pImg + (tty - y) * dstStep; + unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep; + for (int ttx = x; ttx <= r; ++ttx) + { + for (int n = 0; n < dst.channels(); ++n) { + double vtxt = subStr[n] / 255.0; + int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n]; + subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv); + } + + + subStr += 3; + subImg += dst.channels(); + } + } + + + SelectObject(hdc, hOldBmp); + SelectObject(hdc, holdfont); + DeleteObject(hf); + DeleteObject(hBmp); + DeleteDC(hdc); +} + +void drawDefects(cv::Mat& sourceMat, QQueue defectInfoList) +{ + for (int i = 0; i < defectInfoList.size(); i++) + { + defectInfo mdefect = defectInfoList[i]; + + cv::rectangle(sourceMat, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y), + cv::Point(mdefect.defectRect.x + mdefect.defectRect.width, mdefect.defectRect.y + mdefect.defectRect.height), mdefect.defectScalar, 3, 8); + + QByteArray path_arr = mdefect.defectName.toLocal8Bit(); + const char* path = path_arr.data(); + putTextZH(sourceMat, path, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y - 50), mdefect.defectScalar, 50, "微软雅黑"); + } +} + + + +//判断文件夹是否存在,不存在则创建 +void Hd_AI_algo_jiZhuCeLiangSub::isDirExist(QString fullDirName) +{ + QDir* photo = new QDir; + bool exist = photo->exists(fullDirName); + if (exist) + { + //qDebug() << fullDirName << "::已存在"; + } + else + { + //创建photo文件夹 + //qDebug() << fullDirName << "::不存在,创建"; + photo->mkdir(fullDirName); + } + delete photo; + photo = nullptr; +} + +Hd_AI_algo_jiZhuCeLiangSub* create() +{ + qDebug() << "[INFO] " << " Hd_AI_algo_jiZhuCeLiangSub create success!" ; + return new Hd_AI_algo_jiZhuCeLiangSub; +} + +void destory(Hd_AI_algo_jiZhuCeLiangSub* ptr) +{ + qDebug() << "[INFO] " << "Hd_AI_algo_jiZhuCeLiangSub destory success!"; + if(ptr) + { + delete ptr; + ptr = nullptr; + } +} + +Hd_AI_algo_jiZhuCeLiangSub::Hd_AI_algo_jiZhuCeLiangSub() +{ + detect_mutex = new QMutex; + saveStruct_mutex = new QMutex; + ifdetectThread = false; + for (int i = 0; i < 4; i++) + { + QString DirPath = "E:/testImg/source" + QString::number(i); + QStringList imgFilters; + imgFilters << "*.jpg" << "*.png" << "*.PNG" << "*.gif" << "*.tif" << "*.jpeg" << "*.bmp"; + QDir qdir(DirPath); + //返回文件名列表 + QStringList localSourceImgsName = qdir.entryList(imgFilters, QDir::Files | QDir::Readable, QDir::Name); + for (int j = 0; j < localSourceImgsName.size(); j++) + { + QString imgPath = DirPath + "/" + localSourceImgsName[j]; + QFile myf(imgPath); + if (myf.exists()) + myf.remove(); + //Sleep(1); + } + } +} + +Hd_AI_algo_jiZhuCeLiangSub::~Hd_AI_algo_jiZhuCeLiangSub() +{ + ifdetectThread = false; + Sleep(10); + if (detect_mutex) + { + delete detect_mutex; + detect_mutex = nullptr; + } + if (saveStruct_mutex) + { + delete saveStruct_mutex; + saveStruct_mutex = nullptr; + } + ReleaseSharedMemory(); + /*DestroySegObj(pSeg); + DestroyClsObj(pCls);*/ +} +//从类中读数据到实例对象 +bool Hd_AI_algo_jiZhuCeLiangSub::readData(std::vector&vec_mat,QByteArray &data) +{ + detect_mutex->lock(); + //qDebug() << __FUNCTION__ << " line:" <<__LINE__ << " data len:" << data.length(); + if (resMat.size()>0) + { + vec_mat.push_back(resMat[0]); + resMat.clear(); + } + else + { + if(HikMat.size()>0) + vec_mat.push_back(HikMat[0]); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << " resMat.data err:"; + } + //if (allNum++ % 2 == 0) + // data = "2*3*2";// resByte.dequeue(); + //else + data = "1&" + QByteArray::number(algoResult) + "+" + QByteArray::number(algoResult);//1OK,2NG + resByte.clear(); + ////之后根据结果类修改 + //if(resIfNg == true) + // data = "NG"; + //else data = "OK"; + detect_mutex->unlock(); + /*if(cameraKey_index.contains('_')) + qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName:" + << cameraKey_index.split("_")[0] << " imgIndex:" << cameraKey_index.split("_")[1]; + else + qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName imgIndex:" << cameraKey_index;*/ + return true; +} +//从类中读数据到实例对象 +bool Hd_AI_algo_jiZhuCeLiangSub::encodeResultValue(QVector append_value,QByteArray &resultByte) +{ + try + { + std::string sendMsg; + for(int i = 0 ; i < append_value.size();i++) + { + std::string value = append_value.at(1); + //type + int type = 3; + std::string m_type; + m_type.insert(0,4,'1'); + memcpy ( (void*)m_type.c_str(),(void*)&type,4 ); + sendMsg.append(m_type); + + //len + int len = value.length(); + std::string m_len; + m_len.insert(0,4,'1'); + memcpy ( (void*)m_len.c_str(),(void*)&len,4 ); + sendMsg.append(m_len); + + //data x + sendMsg.append(value); + } + resultByte = QByteArray::fromStdString(sendMsg); + } catch (QString ev) + { + qCritical() << "[Error] " << " ev:" << ev; + return false; + } + return true; +} + +int myallNum[4] = {0}; +QMap lastImgMap; +//接收数据 +bool Hd_AI_algo_jiZhuCeLiangSub::writeData(std::vector& vec_mat, QByteArray &data) +{ + //qDebug() << __FUNCTION__ << "line:"<< __LINE__ << " data len" << data.length() ; + if(vec_mat.empty()) + { + qCritical() << "[Error] " << " vec_mat is null !"; + return false; + } + detect_mutex->lock(); + HikMat.clear(); + for (int i = 0; i < vec_mat.size(); i++) + HikMat.enqueue(vec_mat[i]); + + QByteArray source_savePath; + QByteArray sData = data; + QByteArrayList splitDataList = sData.split(','); + if (splitDataList.size() >= 4) + { + if (splitDataList[2].contains('_')) + cameraKey_index = splitDataList[2]; + QByteArray saveDir = splitDataList[0] + "/" + splitDataList[1] + "/"; + isDirExist(byteArrayToUnicode(saveDir)); //不存在,创建 + QString sdataTime= QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz"); + source_savePath = splitDataList[0] + "/" + splitDataList[1] + "/" + sdataTime.toLocal8Bit() + "-" + splitDataList[2] + "-" + splitDataList[3];// +".bmp"; + } + //int index = -1; + /*if (sData.contains("offline-camera0")) + { + index = 0; + savePath = "E:\\testImg\\source0\\"+QString::number(myallNum[0]++) + ".bmp"; + } + else if (sData.contains("offline-camera1")) + { + index = 1; + savePath = "E:\\testImg\\source1\\" + QString::number(myallNum[1]++) + ".bmp"; + } + else if (sData.contains("offline-camera2")) + { + index = 2; + savePath = "E:\\testImg\\source2\\" + QString::number(myallNum[2]++) + ".bmp"; + } + else if (sData.contains("offline-camera3")) + { + index = 3; + savePath = "E:\\testImg\\source3\\" + QString::number(myallNum[3]++) + ".bmp"; + }*/ + + + createDirectoryIfNotExists(oriImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//"); + addImgToSaveStruct(HikMat[0], "0", geteOriSaveFromat());//保存原图 + //if (!source_savePath.isEmpty()) + //{ + // //std::cout << "try savePath::" << savePath.toStdString() << std::endl; + // //测试相机的, + // //while (HikMat[0].cols > 2000)cv::resize(HikMat[0], HikMat[0], cv::Size(), 0.08, 0.08);' + // for (int i = 0; i < initValueList.size(); i++) + // { + // QString minit = byteArrayToUnicode(initValueList[i]); + // if (initValueList[i].contains(data.split(',')[2].split('_')[0])) + // if (initValueList[i].contains(QString("原图_保存").toLocal8Bit())) + // { + // addImgToSaveStruct(HikMat[0], "0", byteArrayToUnicode(source_savePath + ".bmp"));//保存原图 + // if (HikMat.size() == 2)//3D + // addImgToSaveStruct(HikMat[1], "0", byteArrayToUnicode(source_savePath + ".tif")); + // } + // } + //} + + //if (myallNum[index] == 1) + //{ + // cv::Mat img; + // lastImgMap.insert(index, img); + //} + //else + //{ + // //判断和上一张图,是否重复 + // int dataLength = HikMat[0].total() * HikMat[0].elemSize(); + // bool res = false; + // if (myallNum[index] > 0) + // { + // cv::Mat img = lastImgMap.value(index); + // if (img.total() * img.elemSize() == dataLength) + // { + // res = memcmp(img.data, HikMat[0].data, dataLength) == 0; + // if (res == true) + // std::cout << "res ==true ====!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + // } + // } + //} + //lastImgMap.insert(index, HikMat[0]); + + //or检测:保存路径,拍照面描述,相机key_第几张图,SN_时间戳 + //and检测:保存路径,拍照面描述,相机key_第几张图,相机key_第几张图,SN_时间戳,SN_时间戳 + //汇总:保存路径,拍照面描述,触发信号_序号列表0,SN_时间戳 + QString qstring_data = data; + QStringList datalist = qstring_data.split(','); + + + /*saveKey.enqueue("datalist[0]"); + Dir2D.enqueue("datalist[1]"); + QStringList SN_dataTime; + SN_dataTime = datalist[3].split('_'); + DateTime1 = "SN_dataTime[1]"; + SN.enqueue("SN_dataTime[0]");*/ + //qCritical() << __FUNCTION__ << " line: " << __LINE__ << " data:" << qstring_data; + detect_mutex->unlock(); + + //qDebug() << __FUNCTION__ << "line:" << __LINE__ << "[INFO] " << " data: " << data << " run end"; + //cv::Mat resultDstImg = vec_mat[0].clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + //resultDstImg = pSeg->Evaluate(resultDstImg); + //resultDstImg.release(); + + return true; +} + +//传入initParas函数,格式为:相机key+参数名1#参数值1#参数值2+参数名2#参数值1... +//初始化 +bool Hd_AI_algo_jiZhuCeLiangSub::initParas(QByteArray& byte) +{ + initValueList = byte.split('+'); + QStringList valueiList; + + if (byte.contains("onlySetParameter")) + { + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,valueList[0]:" << initValueList[0]; + if (initValueList.size() > 1) + for (int i = 1; i < initValueList.size(); i++) + { + QString valuei = byteArrayToUnicode(initValueList[i]); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,lastName:" << initValueList[0] << " ,valuei:" << valuei; + } + return true; + } + + + if (initValueList.size() > 1) + for (int i = 1; i < initValueList.size(); i++) + { + QString valuei = byteArrayToUnicode(initValueList[i]); + valueiList.append(valuei); + } + + /*pSeg = CreateSegObjS(pSeg_modulePath, 128, gpuId); + pCls = CreateClsObjV3(pCls_modulePath, gpuId, 1);*/ + + + //StationNo = vec_ret.at(0).toInt(); + //InitThreads(StationNo, img2D); + //InitModels(StationNo, img2D); + + //运行检测线程 + ifdetectThread = true; + + //线程检测:结果类完成时,使用 + //ifThreadRunDetect = true; + //QFuture future1 = QtConcurrent::run(this, &Hd_AI_algo_jiZhuCeLiangSub::detectThread); + //运行存图线程 + QFuture future2 = QtConcurrent::run(this, &Hd_AI_algo_jiZhuCeLiangSub::saveImgThread); + //detectThread(); + initSharedMemory(); + //原图总文件夹 + isDirExist(oriImgSaveDir); + //原图class总文件夹 + isDirExist(classImgSaveDir); + //结果图总文件夹 + isDirExist(resImgSaveDir); + + //qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ; + return true; +} +// 解码 +bool Hd_AI_algo_jiZhuCeLiangSub::decodeAiValue(QByteArray byte,QVector &vec_ret) +{ + if(byte.isEmpty()) + { + qCritical() << "[Error] " << " byte is null!"; + return false; + } + + /*******************************************/ + byte.remove(0,8); + QByteArray mm_data = byte.mid(0,4); + memcpy((void*)¶mNum,(void*)mm_data.data(), 4); + qDebug() << "[INFO] " << " paramNum:" << paramNum; + byte.remove(0,4); + /*******************************************/ + + while (!byte.isEmpty()) + { + int type = 0; + QByteArray m_type = byte.mid(0,4); + memcpy((void*)&type,(void*)m_type.data(), 4); + byte.remove(0,4); + + int len = 0; + QByteArray m_len = byte.mid(0,4); + memcpy((void*)&len,(void*)m_len.data(), 4); + byte.remove(0,4); + + QByteArray mm_data = byte.mid(0,len); + vec_ret.append(mm_data); + byte.remove(0,len); + } + return true; +} +//---------检测流程,根据实际项目修改--------- +bool Hd_AI_algo_jiZhuCeLiangSub::AIdetect(cv::Mat img2D, QString mysaveKey, QString myDir2D, QString mySN, QString myDataTime) +{ + try + { + //存图数据加保存队列,加锁,避免数据访问冲突 + saveStruct_mutex->lock(); + saveImgStruct mySave; + Mat sourceImg; + sourceImg = img2D.clone(); + //img2D.copyTo(sourceImg); + //img2D.release(); + QString mysaveKey1 = mysaveKey; //"offlineStation1_off_1" + saveStruct_mutex->unlock(); + + QJsonDocument document; + QJsonObject obj; + obj.insert("result_key", "mysaveKey"); + obj.insert("SN", "mySN"); + obj.insert("DataTime", "myDataTime"); + + // sourceImg = cv::imread("D:/1/2813489.bmp", -1); + sourceImg = cv::imread("D:/1/ruanLianJie/zj/1.bmp",-1); + + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, COLOR_GRAY2BGR); + + + HObject image; + MatToHObject(sourceImg, &image); + while(*sharedUnionCalculatePtr==nullptr) + QThread::msleep(1); + (*sharedUnionCalculatePtr)->insertSubData(image);//发送检测数据到主线程 + + unionCalculate::calResult _calResult; + while ((*sharedUnionCalculatePtr)->getResult(_calResult) == false) + QThread::msleep(1); + if(_calResult.algoFailed == false) + { + //Paint + { + std::string text = "垂直间距:" + std::to_string(_calResult.upDownDistance); + text = text.substr(0, text.find(".") + 4) + "mm"; + putTextHusky(Paint, + utf8_to_gbk(text).c_str(), + cv::Point(50, 50), cv::Scalar(0, 0, 255), 280); + } + { + std::string text = "水平间距:" + std::to_string(_calResult.leftRightDistance); + text = text.substr(0, text.find(".") + 4) + "mm"; + putTextHusky(Paint, + utf8_to_gbk(text).c_str(), + cv::Point(50, 300), cv::Scalar(0, 0, 255), 280); + } + { + std::string text = " 极间距:" + std::to_string(_calResult.interpoleValue); + 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); + } + for (int i = 0; i < _calResult.lines.size() / 4; 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]), + cv::Scalar(0, 255, 0), 20); + for (int i = 0; i < _calResult.points.size() / 3; i++) + cv::circle(Paint, cv::Point(_calResult.points[i * 3 + 1], _calResult.points[i * 3]), + _calResult.points[i * 3 + 2], + cv::Scalar(0, 255, 0), 20); + } + else + putTextHusky(Paint, byteArrayToUnicode("算法错误").toStdString().c_str(), cv::Point(0, 50), cv::Scalar(0, 255, 0), 500); + QQueue defectInfoList; + + + detect_mutex->lock(); + + + + + + resMat.push_back(Paint); + QJsonArray detaillists; + //QByteArrayList resQbyte; + //for (int i = 0; i < defectInfoList.size(); i++) + //{ + // defectInfo mdefect = defectInfoList[i]; + // //resQbyte.push_back(mdefect.defectName.toLocal8Bit()); //中文也可以QString::number(1.1).toUtf8() + // //resQbyte.push_back(QString::number(mdefect.defectRect.x).toUtf8()); + // //resQbyte.push_back(QString::number(mdefect.defectRect.y).toUtf8()); + // //resQbyte.push_back(QString::number(mdefect.defectRect.width).toUtf8()); + // //resQbyte.push_back(QString::number(mdefect.defectRect.height).toUtf8()); + // //resQbyte.push_back(QString::number(mdefect.defectScalar[0]).toUtf8()); + // //resQbyte.push_back(QString::number(mdefect.defectScalar[1]).toUtf8()); + // //resQbyte.push_back(QString::number(mdefect.defectScalar[2]).toUtf8()); + // //resQbyte.push_back(QString::number(mdefect.defectArea).toUtf8()); + // QJsonObject detailObj; + // detailObj.insert("defectName", "mdefect.defectName"); + // detailObj.insert("defectRect.x", QString::number(mdefect.defectRect.x)); + // detailObj.insert("defectRect.y", QString::number(mdefect.defectRect.y)); + // detailObj.insert("defectRect.width", QString::number(mdefect.defectRect.width)); + // detailObj.insert("defectRect.height", QString::number(mdefect.defectRect.height)); + // detailObj.insert("defectScalar0", QString::number(mdefect.defectScalar[0])); + // detailObj.insert("defectScalar1", QString::number(mdefect.defectScalar[1])); + // detailObj.insert("defectScalar2", QString::number(mdefect.defectScalar[2])); + // detailObj.insert("defectArea", QString::number(mdefect.defectArea)); + // detailObj.insert("defectLength", QString::number(sqrt(mdefect.defectRect.width* mdefect.defectRect.width+ mdefect.defectRect.height* mdefect.defectRect.height))); + + // detaillists.append(detailObj); + //} + + + ////使用完删除 + //defectInfoList.clear(); + + //往主界面发 + //emit sendResultData(resMat, resQbyte); + //resQbyte.clear(); + + //isDirExist(resImgSaveDir + mysaveKey1 + "/"); + QString qimgsavePath = resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png"; + //存图数据入队,按需 + if (resMat.size() > 0 ) + { + createDirectoryIfNotExists(resImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//"); + addImgToSaveStruct(resMat[0], "0", getResSaveFromat());//保存原图 + } + detect_mutex->unlock(); + + obj.insert("detaillists", detaillists); + obj.insert("qimgsavePath", qimgsavePath); + document.setObject(obj); + //resByte.clear(); + QByteArray qa = document.toJson(); + //resByte .enqueue(qa); + + + //resMat.release(); + sourceImg.release(); + + mySave.out_Mat.resize(0); + mysaveKey1.clear(); + qApp->processEvents(); + + + ////原图和结果都往主界面发? + //std::vector resMat; + //resMat.push_back(img2D); + //resMat.push_back(sourceRectangles); + //QByteArray resByte = mysaveKey.toLocal8Bit(); + //resByte.append(myDir2D.toLocal8Bit()); + //resByte.append(mySN.toLocal8Bit()); + //resByte.append(myDataTime.toLocal8Bit()); + + ////往主界面发送图片和数据 + ////emit writeData(resMat, resByte); + //resMat.resize(0); + //resByte.resize(0); + } + catch (...) + { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + return false; + } + return true; + +} + +//存图加入队列 +void Hd_AI_algo_jiZhuCeLiangSub::addImgToSaveStruct(cv::Mat img2D, QString mysaveKey, QString savePath) +{ + //存图数据加保存队列,加锁,避免数据访问冲突 + saveStruct_mutex->lock(); + saveImgStruct mySave; + mySave.out_Mat = img2D.clone(); + //img2D.copyTo(mySave.out_Mat); + mySave.saveKey = mysaveKey.toLocal8Bit().toStdString(); + mySave.savePath = savePath.toLocal8Bit().toStdString(); + saveStruct.enqueue(mySave); + saveStruct_mutex->unlock(); +} +void Hd_AI_algo_jiZhuCeLiangSub::saveImgThread() +{ + while (1) + { + if (!ifdetectThread)return; + _sleep(5); + qApp->processEvents(); + if (saveStruct.size() < 1)continue; + for (int i = 0; i < saveStruct.size(); i++) + { + clock_t start, stop; + start = clock(); + saveStruct_mutex->lock(); + saveImgStruct mysaveStruct = saveStruct.dequeue(); + saveStruct_mutex->unlock(); + stop = clock(); + if(stop-start >1000) + std::cout << "lock stop-start >1000::************************" << stop - start << std::endl; + start = clock(); + if (mysaveStruct.out_Mat.data) + imwrite(mysaveStruct.savePath, mysaveStruct.out_Mat); + //std::cout << "savePath::" << mysaveStruct.savePath << std::endl; + stop = clock(); + if (stop - start > 1000) + std::cout << "imwrite stop-start >1000::--------------------------"<< stop - start << std::endl; + _sleep(1); + } + } +} +//检测线程,应当只运行一次 +void Hd_AI_algo_jiZhuCeLiangSub::detectThread() +{ + do + { + if (!ifdetectThread)return; + + //此两行代码,避免程序卡死;_sleep的时间根据实际项目调整 + _sleep(2); + qApp->processEvents(); + //取数据加锁,避免数据访问冲突 + detect_mutex->lock(); + //if (HikMat.size() < 1 || saveKey.size() < 1 || Dir2D.size() < 1 || SN.size() < 1 )//|| DateTime1.size() < 1) + //{ + // detect_mutex->unlock(); + // continue; + //} + + cv::Mat sourceImg1 = HikMat[0];// .dequeue(); + QString mysaveKey = "saveKey";// .dequeue(); + QString myDir2D = "Dir2D";// .dequeue(); + QString mySN = "SN";// .dequeue(); + QString myDataTime = "DateTime1"; + detect_mutex->unlock(); + + //判断存图文件夹是否存在,不存在则创建 + { + //原图总文件夹 + isDirExist(oriImgSaveDir + mysaveKey); + //原图class总文件夹 + isDirExist(classImgSaveDir + mysaveKey); + //结果图总文件夹 + isDirExist(resImgSaveDir + mysaveKey); + + } + + //测试相机的, + // while (sourceImg1.cols > 2000)cv::resize(sourceImg1, sourceImg1, cv::Size(), 0.5, 0.5); + + //存图数据入队 + //addImgToSaveStruct(sourceImg1, mysaveKey, oriImgSaveDir + mysaveKey + "/" + mySN + myDataTime + ".bmp"); + + //-------检测流程,根据实际项目修改--------- + AIdetect(sourceImg1, mysaveKey, oriImgSaveDir, mySN, myDataTime); + //sourceImg1.release(); + + mysaveKey.clear(); + myDir2D.clear(); + mySN.clear(); + myDataTime.clear(); + qApp->processEvents(); + + } while (ifThreadRunDetect); + +} + +static int runNum = 0; +bool Hd_AI_algo_jiZhuCeLiangSub::run() +{ + clock_t start, stop; + start = clock(); + + //Sleep(1000); + detectThread(); + stop = clock(); + qDebug() << __FUNCTION__ << "line:" << __LINE__ << " run start ::runNum::" << runNum++<<" cutTime::"< +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "imgproc/imgproc_c.h" +#include "base.h" +#include "handleAI.h" +#pragma execution_character_set("utf-8") + + + +//保存图片的结构体 +struct saveImgStruct; +//{ +// cv::Mat out_Mat; +// std::string saveKey; //关键字,用于LOG日志 +// std::string savePath; //保存路径 +//}; + +//初始化流程(一次):Hd_AI_algo_jiZhuCeLiangSub,initParas +//运行流程:writeData主线程把数据传过来,run,readData此线程把数据传主线程 +class Hd_AI_algo_jiZhuCeLiangSub :public Base +{ + Q_OBJECT +public: + Hd_AI_algo_jiZhuCeLiangSub(); + virtual ~Hd_AI_algo_jiZhuCeLiangSub(); + //从类中读数据到实例对象 + bool readData(std::vector&,QByteArray&); + //实例对象把数据写入到类 + bool writeData(std::vector&, QByteArray&); + bool initParas(QByteArray&); + //运行(Logic_taskthread.cpp) + bool run(); + //线程状态 + bool checkStatus();// { return true; }; + + bool encodeResultValue(QVector append_value,QByteArray &resultByte); + bool decodeAiValue(QByteArray byte,QVector &vec_ret); + //---------检测流程,根据实际项目修改--------- + bool AIdetect(cv::Mat img2D, QString saveKey, QString Dir2D, QString SN, QString DataTime); + +private: + int allNum = 0; + int StationNo=0; + int img2D = 2; + + bool moduleStatus = true; + + //输入图片队列 + QQueue HikMat; + //输入关键字队列 + QString saveKey="saveKey"; + //结果路径文件夹队列 + QString Dir2D="Dir2D"; + //产品SN队列 + QString SN="SN"; + //产品时间戳队列 + QString DateTime1="DateTime1"; + + int paramNum; + + //是否使用线程检测方式 + bool ifThreadRunDetect = false; + //检测结果图 + std::vector resMat; + //检测结果 + bool resIfNg = false; + + QQueue resByte; + + //检测线程 + void detectThread(); + //存图线程 + void saveImgThread(); + + //保存图片队列 + QQueue saveStruct; + + //判断文件夹是否存在,不存在则创建 + void isDirExist(QString fullDirName); + + //存图加入队列 + void addImgToSaveStruct(cv::Mat img2D, QString saveKey, QString savePath); +}; + +extern "C" +{ + Q_DECL_EXPORT Hd_AI_algo_jiZhuCeLiangSub * create(); + Q_DECL_EXPORT void destory(Hd_AI_algo_jiZhuCeLiangSub * ptr); +} +//保存图片的结构体 +struct saveImgStruct +{ + cv::Mat out_Mat; + std::string saveKey; //关键字,用于LOG日志 + std::string savePath; //保存路径 +}; +#endif // Hd_AI_927_2D_module_H diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/base.h b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/base.h new file mode 100644 index 0000000..00bd6b2 --- /dev/null +++ b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/base.h @@ -0,0 +1,69 @@ +#ifndef __BASECLASS_H__ +#define __BASECLASS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Base :public QObject +{ + Q_OBJECT +public: + bool init_flag; //是否初始化,false,true + bool status = true; //相机状态(判断相机是否断开连接) + std::string paras; //传输数据 + std::vector m_mat; //传输图片 + std::string moduleName; //模块名称 +public: + Base(){} + virtual ~Base(){} + virtual bool readData(std::vector&,QByteArray&){return true;} + virtual bool writeData(std::vector&,QByteArray&){return true;} + virtual bool initParas(QByteArray&){return true;} + virtual bool run() {return true;} + virtual bool checkStatus() { return true; } + +//signals: + //QByteArrayList,根据实际项目进行解析;因不确定每个参数的长度,固用队列的方式。每个QByteArray表示一个值,使用时先转换为QString + //参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double) + //void sendResultData(cv::Mat resMat, QJsonObject resQbj); +}; + +/* +//QJsonObject 转QByteArray +{ + QJsonDocument document; + QJsonObject obj; + obj.insert(password, "pwd"); + obj.insert(userName, "usr"); + document.setObject(obj); + QByteArray abyte = document.toJson(QJsonDocument::Compact); +} + +//QByteArray转QJsonObject +{ + QByteArray ba = reply->readAll(); + QJsonDocument doc = QJsonDocument::fromJson(ba); + QJsonObject obj = doc.object(); +} +*/ +/* +//从类中读数据到实例对象 +virtual bool readData(std::vector&,QByteArray&){return true;} + + + + + + + + +*/ + +#endif diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/sharedMen_Sub.h b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/sharedMen_Sub.h new file mode 100644 index 0000000..20c7bd8 --- /dev/null +++ b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/sharedMen_Sub.h @@ -0,0 +1,41 @@ +#include +#include +#include +#include // 包含unionCalculate类的定义 + +// 全局变量 +HANDLE hMapFile; +unionCalculate** sharedUnionCalculatePtr = nullptr; + +// 导出函数:获取 unionCalculate 实例并启动线程 +extern "C" __declspec(dllexport) void initSharedMemory() { + // 打开现有的共享内存 + hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "unionCalculatePtr"); + + if (hMapFile == NULL) { + std::cerr << "Could not open file mapping object (" << GetLastError() << ")" << std::endl; + return; + } + + sharedUnionCalculatePtr = (unionCalculate**)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(unionCalculate*)); + + if (sharedUnionCalculatePtr == NULL) { + std::cerr << "Could not map view of file (" << GetLastError() << ")" << std::endl; + CloseHandle(hMapFile); + return; + } + + std::cout << "DLL B: Acquired unionCalculate instance" << std::endl; + +} + +// 导出函数:停止线程并释放共享内存 +extern "C" __declspec(dllexport) void ReleaseSharedMemory() { + + if (sharedUnionCalculatePtr) { + UnmapViewOfFile(sharedUnionCalculatePtr); + } + if (hMapFile) { + CloseHandle(hMapFile); + } +} diff --git a/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/铜极 b/BaseModules/Hd_AI_algo_jiZhuCeLiangSub/铜极 new file mode 100644 index 0000000..e69de29 diff --git a/BaseModules/Hd_AI_algo_maJiaReRong/CMakeLists.txt b/BaseModules/Hd_AI_algo_maJiaReRong/CMakeLists.txt new file mode 100644 index 0000000..be2265d --- /dev/null +++ b/BaseModules/Hd_AI_algo_maJiaReRong/CMakeLists.txt @@ -0,0 +1,71 @@ +# 获取获取当前目录名 +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" "halconAlgo" "myTools" "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( + + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) + + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC ON) +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 REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS network REQUIRED) +add_library(${CURRENT_FOLDER} SHARED + ${HEAD_FILES} + ${SRC_FILES} + ${UI_FILES} + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES LINKER_LANGUAGE C + 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}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " model end : " ${CURRENT_FOLDER}:${RUN_TIME}) diff --git a/BaseModules/Hd_AI_algo_maJiaReRong/Hd_AI_algo_maJiaReRong.cpp b/BaseModules/Hd_AI_algo_maJiaReRong/Hd_AI_algo_maJiaReRong.cpp new file mode 100644 index 0000000..f8fc0a5 --- /dev/null +++ b/BaseModules/Hd_AI_algo_maJiaReRong/Hd_AI_algo_maJiaReRong.cpp @@ -0,0 +1,1079 @@ +#include "Hd_AI_algo_maJiaReRong.h" +#include + +#include +#include +#include +#include +#include +#include +using namespace cv; +//原图保存文件夹s + QString oriImgSaveDir = "D:/601线/软连接CCD/马甲热熔CCD/相机1/原图/"; +//原图class图保存文件夹 + QString classImgSaveDir = "D:/601线/软连接CCD/马甲热熔CCD/相机1/class图/"; +//结果保存文件夹 + QString resImgSaveDir = "D:/601线/软连接CCD/马甲热熔CCD/相机1/结果图/"; + int algoResult = 0;//0 OK 1 NG +//切图的class的width,根据项目实际设置! + int sizeW = 150; +//切图的class的height,根据项目实际设置! + int sizeH = 150; +//切图的class的width,根据项目实际设置!0为黑,255为白; + int fillColour = 0; + + +//检测线程锁 + QMutex* detect_mutex; +//保存线程锁 + QMutex* saveStruct_mutex; +//检测线程运行标志 + bool ifdetectThread = false; + + QString cameraKey_index = ""; + + //初始化参数列表 + QByteArrayList initValueList; + + static void createDirectoryIfNotExists(const QString& path) { + QDir dir; + dir.mkpath(path); + } + static std::string getCurrentTime() { + + // 获取当前时间点 + auto now = std::chrono::system_clock::now(); + + // 转换为字符串 + auto currentTime = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 100; + std::tm* localTime = std::localtime(¤tTime); + std::stringstream ss; + ss << std::put_time(localTime, "%H-%M-%S") << "-" << std::setfill('0') << std::setw(2) << ms.count(); + return ss.str(); + } + static QString getCurrentDate() + { + QDateTime currentDateTime = QDateTime::currentDateTime(); + return currentDateTime.toString("yyyy-MM-dd"); + } + static QString getCurrentHour() + { + QDateTime currentDateTime = QDateTime::currentDateTime(); + return QString::number(currentDateTime.time().hour())+"H"; + } + QString byteArrayToUnicode(const QByteArray array) { + + // state用于保存转换状态,它的成员invalidChars,可用来判断是否转换成功 + // 如果转换成功,则值为0,如果值大于0,则说明转换失败 + QTextCodec::ConverterState state; + // 先尝试使用utf-8的方式把QByteArray转换成QString + QString text = QTextCodec::codecForName("UTF-8")->toUnicode(array.constData(), array.size(), &state); + // 如果转换时无效字符数量大于0,说明编码格式不对 + if (state.invalidChars > 0) + { + // 再尝试使用GBK的方式进行转换,一般就能转换正确(当然也可能是其它格式,但比较少见了) + text = QTextCodec::codecForName("GBK")->toUnicode(array); + } + return text; + } + static cv::Rect findMaxAreaContour(cv::Mat src) + { + int maxIndex = 0, maxArea = 0; + std::vector>contours; + cv::findContours(src, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + if (contours.size() == 0) + return cv::Rect(0, 0, 0, 0); + for (int i = 0; i < contours.size(); i++) + { + int Area = cv::contourArea(contours[i]); + if (Area > maxArea) + { + maxArea = Area; + maxIndex = i; + } + } + if (contours.size() != 0) + return cv::boundingRect(contours[maxIndex]); + return cv::Rect(0, 0, 0, 0); + } + inline std::string utf8_to_gbk(const std::string& str) + { + std::wstring_convert> conv; + std::wstring tmp_wstr = conv.from_bytes(str); + //GBK locale name in windows + const char* GBK_LOCALE_NAME = ".936"; + std::wstring_convert> convert(new std::codecvt_byname(GBK_LOCALE_NAME)); + return convert.to_bytes(tmp_wstr); + } + void getAllFiles(std::string path, std::vector& files, std::string format) + { + intptr_t hFile = 0;//文件句柄 + struct _finddata_t fileinfo;//文件信息 + std::string p; + if ((hFile = _findfirst(p.assign(path).append("\\*" + format).c_str(), &fileinfo)) != -1) //文件存在 + { + do + { + if ((fileinfo.attrib & _A_SUBDIR))//判断是否为文件夹 + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)//文件夹名中不含"."和".." + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name)); //保存文件夹名 + getAllFiles(p.assign(path).append("\\").append(fileinfo.name), files, format); //递归遍历文件夹 + } + } + else + { + files.push_back(p.assign(path).append("\\").append(fileinfo.name));//如果不是文件夹,储存文件名 + } + } while (_findnext(hFile, &fileinfo) == 0); + _findclose(hFile); + } + } + QString geteOriSaveFromat() + { + return oriImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//" + + "ORI_" + cameraKey_index + "_" + QString::fromStdString(getCurrentTime()) + ".bmp"; + } + QString getResSaveFromat() + { + return resImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//" + + "RES_" + cameraKey_index + "_" + QString::fromStdString(getCurrentTime()) + ".png"; + } +// 缺陷信息结构体 +typedef struct { + QString defectName = "";//缺陷名称 + cv::Rect defectRect = { 0,0,0,0 };//缺陷位置 + cv::Scalar defectScalar = cv::Scalar(0, 0, 0);//画框颜色 + double defectArea = 0;//缺陷真实面积 + double defectDepth = 0;//缺陷深度/高度,3D信息 +}defectInfo; + +//Mat转Halcon图像 +void MatToHObject(cv::Mat& image, HalconCpp::HObject* Hobj) +{ + int hgt = image.rows; + int wid = image.cols; + int i; + if (image.type() == CV_8UC3) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + std::vector imgchannel; + split(image, imgchannel); + cv::Mat imgB = imgchannel[0]; + cv::Mat imgG = imgchannel[1]; + cv::Mat imgR = imgchannel[2]; + uchar* dataR = new uchar[hgt * wid]; + uchar* dataG = new uchar[hgt * wid]; + uchar* dataB = new uchar[hgt * wid]; + for (i = 0; i < hgt; i++) + { + memcpy(dataR + wid * i, imgR.data + imgR.step * i, wid); + memcpy(dataG + wid * i, imgG.data + imgG.step * i, wid); + memcpy(dataB + wid * i, imgB.data + imgB.step * i, wid); + } + himage.GenImage3("byte", wid, hgt, dataR, dataG, dataB); + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPtR, * hImgPtG, * hImgPtB; + himage.GetImagePointer3(&hImgPtR, &hImgPtG, &hImgPtB, &tmpType, &tmpW, &tmpH); + + GenImage3(Hobj, "byte", wid, hgt, (Hlong)hImgPtR, (Hlong)hImgPtG, (Hlong)hImgPtB); + delete[]dataR; + delete[]dataG; + delete[]dataB; + dataR = NULL; + dataG = NULL; + dataB = NULL; + } + else if (image.type() == CV_8UC1) + { + //HalconCpp::HImage himage = HalconCpp::HImage(); + + ////uchar* data = new uchar[hgt * wid]; + ////for (i = 0; i < hgt; i++) + ////{ + //// memcpy(data + wid * i, image.data + image.step * i, wid); + ////} + ////gen_image_const + //himage.GenImage1("byte", wid, hgt, image.data); + + //HalconCpp::HString tmpType; + //Hlong tmpW, tmpH; + //void* hImgPt; + //hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + //GenImage1(&Hobj, "byte", wid, hgt, (Hlong)hImgPt); + GenImage1(Hobj, "byte", wid, hgt, (Hlong)image.data); + + /* delete[] data; + data = NULL;*/ + + } + else if (image.type() == CV_16UC1) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + ushort* data = new ushort[hgt * wid * 2]; + + memcpy(data, image.data, hgt * wid * 2); + + + himage.GenImage1("uint2", wid, hgt, data); + + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPt; + hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + GenImage1(Hobj, "uint2", wid, hgt, (Hlong)hImgPt); + + delete[] data; + data = NULL; + + } + else if (image.type() == CV_16SC1) + { + HalconCpp::HImage himage = HalconCpp::HImage(); + + short* data = new short[hgt * wid * 2]; + + memcpy(data, image.data, hgt * wid * 2); + + + himage.GenImage1("int2", wid, hgt, data); + + HalconCpp::HString tmpType; + Hlong tmpW, tmpH; + void* hImgPt; + hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); + GenImage1(Hobj, "int2", wid, hgt, (Hlong)hImgPt); + + delete[] data; + data = NULL; + + } + +} + +//判断文件是否存在,不存在则创建 +bool isFileExist(QString fullFileName) +{ + QFileInfo fileInfo(fullFileName); + if (fileInfo.isFile()) + { + return true; + } + return false; +} + +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 putTextZH(cv::Mat& dst, const char* str, cv::Point org, cv::Scalar color, int fontsize, + const char* fn = "Arial", bool italic = false, bool underline = false) +{ + CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3)); + + + int x, y, r, b; + //坐标点大于图像宽和高直接返回 + if (org.x > dst.cols || org.y > dst.rows) return; + x = org.x < 0 ? -org.x : 0; + y = org.y < 0 ? -org.y : 0; + + + LOGFONTA lf; + lf.lfHeight = -fontsize; //字体高度 + lf.lfWidth = 0; //平均宽度 + lf.lfEscapement = 0; //字符排列角度 + lf.lfOrientation = 0; //字符本身旋转的角度 + lf.lfWeight = 5; //设置字体线条的宽度 + lf.lfItalic = italic; //斜体 + lf.lfUnderline = underline; //下划线 + lf.lfStrikeOut = 0; //是否字符中央加横线 + lf.lfCharSet = DEFAULT_CHARSET; //字符集 + lf.lfOutPrecision = 0; //字体的精确度 + lf.lfQuality = PROOF_QUALITY; //字体质量 + lf.lfPitchAndFamily = 0; //选择字体的间距和字体家族 + strcpy_s(lf.lfFaceName, fn); //字体的名称 + + + HFONT hf = CreateFontIndirectA(&lf); + HDC hdc = CreateCompatibleDC(0); + HFONT holdfont = (HFONT)SelectObject(hdc, hf); + + + int strBaseW = 0, strBaseH = 0; + int singleRow = 0; + char buf[1 << 12]; + strcpy_s(buf, str); + char* bufT[1 << 12]; //这个用于分隔字符串后剩余的字符,可能会超出。 + + + //处理多行 + { + int nnh = 0; + int cw, ch; + + + const char* ln = strtok_s(buf, "\n", bufT); + while (ln != 0) + { + GetStringSize(hdc, ln, &cw, &ch); + strBaseW = max(strBaseW, cw); + strBaseH = max(strBaseH, ch); + + + ln = strtok_s(0, "\n", bufT); + nnh++; + } + singleRow = strBaseH; + strBaseH *= nnh; + } + + + if (org.x + strBaseW < 0 || org.y + strBaseH < 0) + { + SelectObject(hdc, holdfont); + DeleteObject(hf); + DeleteObject(hdc); + return; + } + + + r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1; + b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1; + org.x = org.x < 0 ? 0 : org.x; + org.y = org.y < 0 ? 0 : org.y; + + + BITMAPINFO bmp = { 0 }; + BITMAPINFOHEADER& bih = bmp.bmiHeader; + int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4)); + + + bih.biSize = sizeof(BITMAPINFOHEADER); + bih.biWidth = strBaseW; + bih.biHeight = strBaseH; + bih.biPlanes = 1; + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + bih.biSizeImage = strBaseH * strDrawLineStep; + bih.biClrUsed = 0; + bih.biClrImportant = 0; + + + void* pDibData = 0; + HBITMAP hBmp = CreateDIBSection(hdc, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0); + + + CV_Assert(pDibData != 0); + HBITMAP hOldBmp = (HBITMAP)SelectObject(hdc, hBmp); + + + //color.val[2], color.val[1], color.val[0] + SetTextColor(hdc, RGB(255, 255, 255)); + SetBkColor(hdc, 0); + //SetStretchBltMode(hDC, COLORONCOLOR); + + + strcpy_s(buf, str); + const char* ln = strtok_s(buf, "\n", bufT); + int outTextY = 0; + while (ln != 0) + { + TextOutA(hdc, 0, outTextY, ln, strlen(ln)); + outTextY += singleRow; + ln = strtok_s(0, "\n", bufT); + } + uchar* dstData = (uchar*)dst.data; + int dstStep = dst.step / sizeof(dstData[0]); + unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep; + unsigned char* pStr = (unsigned char*)pDibData + x * 3; + for (int tty = y; tty <= b; ++tty) + { + unsigned char* subImg = pImg + (tty - y) * dstStep; + unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep; + for (int ttx = x; ttx <= r; ++ttx) + { + for (int n = 0; n < dst.channels(); ++n) { + double vtxt = subStr[n] / 255.0; + int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n]; + subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv); + } + + + subStr += 3; + subImg += dst.channels(); + } + } + + + SelectObject(hdc, hOldBmp); + SelectObject(hdc, holdfont); + DeleteObject(hf); + DeleteObject(hBmp); + DeleteDC(hdc); +} + +void drawDefects(cv::Mat& sourceMat, QQueue defectInfoList) +{ + for (int i = 0; i < defectInfoList.size(); i++) + { + defectInfo mdefect = defectInfoList[i]; + + cv::rectangle(sourceMat, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y), + cv::Point(mdefect.defectRect.x + mdefect.defectRect.width, mdefect.defectRect.y + mdefect.defectRect.height), mdefect.defectScalar, 3, 8); + + QByteArray path_arr = mdefect.defectName.toLocal8Bit(); + const char* path = path_arr.data(); + putTextZH(sourceMat, path, cv::Point(mdefect.defectRect.x, mdefect.defectRect.y - 50), mdefect.defectScalar, 50, "微软雅黑"); + } +} + + + +//判断文件夹是否存在,不存在则创建 +void Hd_AI_algo_maJiaReRong::isDirExist(QString fullDirName) +{ + QDir* photo = new QDir; + bool exist = photo->exists(fullDirName); + if (exist) + { + //qDebug() << fullDirName << "::已存在"; + } + else + { + //创建photo文件夹 + //qDebug() << fullDirName << "::不存在,创建"; + photo->mkdir(fullDirName); + } + delete photo; + photo = nullptr; +} + +Hd_AI_algo_maJiaReRong* create() +{ + qDebug() << "[INFO] " << " Hd_AI_algo_maJiaReRong create success!" ; + return new Hd_AI_algo_maJiaReRong; +} + +void destory(Hd_AI_algo_maJiaReRong* ptr) +{ + qDebug() << "[INFO] " << "Hd_AI_algo_maJiaReRong destory success!"; + if(ptr) + { + delete ptr; + ptr = nullptr; + } +} + +Hd_AI_algo_maJiaReRong::Hd_AI_algo_maJiaReRong() +{ + detect_mutex = new QMutex; + saveStruct_mutex = new QMutex; + ifdetectThread = false; + for (int i = 0; i < 4; i++) + { + QString DirPath = "E:/testImg/source" + QString::number(i); + QStringList imgFilters; + imgFilters << "*.jpg" << "*.png" << "*.PNG" << "*.gif" << "*.tif" << "*.jpeg" << "*.bmp"; + QDir qdir(DirPath); + //返回文件名列表 + QStringList localSourceImgsName = qdir.entryList(imgFilters, QDir::Files | QDir::Readable, QDir::Name); + for (int j = 0; j < localSourceImgsName.size(); j++) + { + QString imgPath = DirPath + "/" + localSourceImgsName[j]; + QFile myf(imgPath); + if (myf.exists()) + myf.remove(); + //Sleep(1); + } + } +} + +Hd_AI_algo_maJiaReRong::~Hd_AI_algo_maJiaReRong() +{ + ifdetectThread = false; + Sleep(10); + if (detect_mutex) + { + delete detect_mutex; + detect_mutex = nullptr; + } + if (saveStruct_mutex) + { + delete saveStruct_mutex; + saveStruct_mutex = nullptr; + } + + /*DestroySegObj(pSeg); + DestroyClsObj(pCls);*/ +} +//从类中读数据到实例对象 +bool Hd_AI_algo_maJiaReRong::readData(std::vector&vec_mat,QByteArray &data) +{ + detect_mutex->lock(); + //qDebug() << __FUNCTION__ << " line:" <<__LINE__ << " data len:" << data.length(); + if (resMat.size()>0) + { + vec_mat.push_back(resMat[0]); + resMat.clear(); + } + else + { + if(HikMat.size()>0) + vec_mat.push_back(HikMat[0]); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << " resMat.data err:"; + } + //if (allNum++ % 2 == 0) + // data = "2*3*2";// resByte.dequeue(); + //else + data = "1*" + QByteArray::number(algoResult) + "&+" + QByteArray::number(algoResult);//1OK,2NG + resByte.clear(); + ////之后根据结果类修改 + //if(resIfNg == true) + // data = "NG"; + //else data = "OK"; + detect_mutex->unlock(); + /*if(cameraKey_index.contains('_')) + qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName:" + << cameraKey_index.split("_")[0] << " imgIndex:" << cameraKey_index.split("_")[1]; + else + qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getDetectData--CameraName imgIndex:" << cameraKey_index;*/ + return true; +} +//从类中读数据到实例对象 +bool Hd_AI_algo_maJiaReRong::encodeResultValue(QVector append_value,QByteArray &resultByte) +{ + try + { + std::string sendMsg; + for(int i = 0 ; i < append_value.size();i++) + { + std::string value = append_value.at(1); + //type + int type = 3; + std::string m_type; + m_type.insert(0,4,'1'); + memcpy ( (void*)m_type.c_str(),(void*)&type,4 ); + sendMsg.append(m_type); + + //len + int len = value.length(); + std::string m_len; + m_len.insert(0,4,'1'); + memcpy ( (void*)m_len.c_str(),(void*)&len,4 ); + sendMsg.append(m_len); + + //data x + sendMsg.append(value); + } + resultByte = QByteArray::fromStdString(sendMsg); + } catch (QString ev) + { + qCritical() << "[Error] " << " ev:" << ev; + return false; + } + return true; +} + +int myallNum[4] = {0}; +QMap lastImgMap; +//接收数据 +bool Hd_AI_algo_maJiaReRong::writeData(std::vector& vec_mat, QByteArray &data) +{ + //qDebug() << __FUNCTION__ << "line:"<< __LINE__ << " data len" << data.length() ; + if(vec_mat.empty()) + { + qCritical() << "[Error] " << " vec_mat is null !"; + return false; + } + detect_mutex->lock(); + HikMat.clear(); + for (int i = 0; i < vec_mat.size(); i++) + HikMat.enqueue(vec_mat[i]); + + QByteArray source_savePath; + QByteArray sData = data; + QByteArrayList splitDataList = sData.split(','); + if (splitDataList.size() >= 4) + { + if (splitDataList[2].contains('_')) + cameraKey_index = splitDataList[2]; + QByteArray saveDir = splitDataList[0] + "/" + splitDataList[1] + "/"; + isDirExist(byteArrayToUnicode(saveDir)); //不存在,创建 + QString sdataTime= QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz"); + source_savePath = splitDataList[0] + "/" + splitDataList[1] + "/" + sdataTime.toLocal8Bit() + "-" + splitDataList[2] + "-" + splitDataList[3];// +".bmp"; + } + //int index = -1; + /*if (sData.contains("offline-camera0")) + { + index = 0; + savePath = "E:\\testImg\\source0\\"+QString::number(myallNum[0]++) + ".bmp"; + } + else if (sData.contains("offline-camera1")) + { + index = 1; + savePath = "E:\\testImg\\source1\\" + QString::number(myallNum[1]++) + ".bmp"; + } + else if (sData.contains("offline-camera2")) + { + index = 2; + savePath = "E:\\testImg\\source2\\" + QString::number(myallNum[2]++) + ".bmp"; + } + else if (sData.contains("offline-camera3")) + { + index = 3; + savePath = "E:\\testImg\\source3\\" + QString::number(myallNum[3]++) + ".bmp"; + }*/ + + + createDirectoryIfNotExists(oriImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//"); + addImgToSaveStruct(HikMat[0], "0", geteOriSaveFromat());//保存原图 + //if (!source_savePath.isEmpty()) + //{ + // //std::cout << "try savePath::" << savePath.toStdString() << std::endl; + // //测试相机的, + // //while (HikMat[0].cols > 2000)cv::resize(HikMat[0], HikMat[0], cv::Size(), 0.08, 0.08);' + // for (int i = 0; i < initValueList.size(); i++) + // { + // QString minit = byteArrayToUnicode(initValueList[i]); + // if (initValueList[i].contains(data.split(',')[2].split('_')[0])) + // if (initValueList[i].contains(QString("原图_保存").toLocal8Bit())) + // { + // addImgToSaveStruct(HikMat[0], "0", byteArrayToUnicode(source_savePath + ".bmp"));//保存原图 + // if (HikMat.size() == 2)//3D + // addImgToSaveStruct(HikMat[1], "0", byteArrayToUnicode(source_savePath + ".tif")); + // } + // } + //} + + //if (myallNum[index] == 1) + //{ + // cv::Mat img; + // lastImgMap.insert(index, img); + //} + //else + //{ + // //判断和上一张图,是否重复 + // int dataLength = HikMat[0].total() * HikMat[0].elemSize(); + // bool res = false; + // if (myallNum[index] > 0) + // { + // cv::Mat img = lastImgMap.value(index); + // if (img.total() * img.elemSize() == dataLength) + // { + // res = memcmp(img.data, HikMat[0].data, dataLength) == 0; + // if (res == true) + // std::cout << "res ==true ====!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; + // } + // } + //} + //lastImgMap.insert(index, HikMat[0]); + + //or检测:保存路径,拍照面描述,相机key_第几张图,SN_时间戳 + //and检测:保存路径,拍照面描述,相机key_第几张图,相机key_第几张图,SN_时间戳,SN_时间戳 + //汇总:保存路径,拍照面描述,触发信号_序号列表0,SN_时间戳 + QString qstring_data = data; + QStringList datalist = qstring_data.split(','); + + + /*saveKey.enqueue("datalist[0]"); + Dir2D.enqueue("datalist[1]"); + QStringList SN_dataTime; + SN_dataTime = datalist[3].split('_'); + DateTime1 = "SN_dataTime[1]"; + SN.enqueue("SN_dataTime[0]");*/ + //qCritical() << __FUNCTION__ << " line: " << __LINE__ << " data:" << qstring_data; + detect_mutex->unlock(); + + //qDebug() << __FUNCTION__ << "line:" << __LINE__ << "[INFO] " << " data: " << data << " run end"; + //cv::Mat resultDstImg = vec_mat[0].clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + //resultDstImg = pSeg->Evaluate(resultDstImg); + //resultDstImg.release(); + + return true; +} + +//传入initParas函数,格式为:相机key+参数名1#参数值1#参数值2+参数名2#参数值1... +//初始化 +bool Hd_AI_algo_maJiaReRong::initParas(QByteArray& byte) +{ + initValueList = byte.split('+'); + QStringList valueiList; + + if (byte.contains("onlySetParameter")) + { + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,valueList[0]:" << initValueList[0]; + if (initValueList.size() > 1) + for (int i = 1; i < initValueList.size(); i++) + { + QString valuei = byteArrayToUnicode(initValueList[i]); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,lastName:" << initValueList[0] << " ,valuei:" << valuei; + } + return true; + } + + + if (initValueList.size() > 1) + for (int i = 1; i < initValueList.size(); i++) + { + QString valuei = byteArrayToUnicode(initValueList[i]); + valueiList.append(valuei); + } + + /*pSeg = CreateSegObjS(pSeg_modulePath, 128, gpuId); + pCls = CreateClsObjV3(pCls_modulePath, gpuId, 1);*/ + + + //StationNo = vec_ret.at(0).toInt(); + //InitThreads(StationNo, img2D); + //InitModels(StationNo, img2D); + + //运行检测线程 + ifdetectThread = true; + + //线程检测:结果类完成时,使用 + //ifThreadRunDetect = true; + //QFuture future1 = QtConcurrent::run(this, &Hd_AI_algo_maJiaReRong::detectThread); + //运行存图线程 + QFuture future2 = QtConcurrent::run(this, &Hd_AI_algo_maJiaReRong::saveImgThread); + //detectThread(); + //seg_hanHouDingWei = handdleAI::CreateSegObjS("./model/seg-hanHouDingWei.BHD",32,0); + //原图总文件夹 + isDirExist(oriImgSaveDir); + //原图class总文件夹 + isDirExist(classImgSaveDir); + //结果图总文件夹 + isDirExist(resImgSaveDir); + + //qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ; + return true; +} +// 解码 +bool Hd_AI_algo_maJiaReRong::decodeAiValue(QByteArray byte,QVector &vec_ret) +{ + if(byte.isEmpty()) + { + qCritical() << "[Error] " << " byte is null!"; + return false; + } + + /*******************************************/ + byte.remove(0,8); + QByteArray mm_data = byte.mid(0,4); + memcpy((void*)¶mNum,(void*)mm_data.data(), 4); + qDebug() << "[INFO] " << " paramNum:" << paramNum; + byte.remove(0,4); + /*******************************************/ + + while (!byte.isEmpty()) + { + int type = 0; + QByteArray m_type = byte.mid(0,4); + memcpy((void*)&type,(void*)m_type.data(), 4); + byte.remove(0,4); + + int len = 0; + QByteArray m_len = byte.mid(0,4); + memcpy((void*)&len,(void*)m_len.data(), 4); + byte.remove(0,4); + + QByteArray mm_data = byte.mid(0,len); + vec_ret.append(mm_data); + byte.remove(0,len); + } + return true; +} +//---------检测流程,根据实际项目修改--------- +bool Hd_AI_algo_maJiaReRong::AIdetect(cv::Mat img2D, QString mysaveKey, QString myDir2D, QString mySN, QString myDataTime) +{ + try + { + //存图数据加保存队列,加锁,避免数据访问冲突 + saveStruct_mutex->lock(); + saveImgStruct mySave; + Mat sourceImg; + sourceImg = img2D.clone(); + //img2D.copyTo(sourceImg); + //img2D.release(); + QString mysaveKey1 = mysaveKey; //"offlineStation1_off_1" + saveStruct_mutex->unlock(); + + QJsonDocument document; + QJsonObject obj; + obj.insert("result_key", "mysaveKey"); + obj.insert("SN", "mySN"); + obj.insert("DataTime", "myDataTime"); + + + //QFile csvName("test.csv"); + //QStringList lines; + //lines << "class,name\n" << "3,4,5,6,7\n" << "a,b,c,d\n"; + //if (csvName.open(QIODevice::WriteOnly)) //若文件不存在,会自动创建 + //{ + // for (int i = 0; i < lines.size(); i++) + // csvName.write(lines[i].toStdString().c_str()); + // csvName.close(); + //} + //sourceImg = cv::imread("D:\\mySN15-46-33-86.bmp",-1); + //sourceImg = cv::imread("D:/CCD/Image_20241017202132775.bmp", -1); + sourceImg = cv::imread("D:/1/majia/Image_20241016210237741.bmp",-1); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if(Paint.channels() == 1) + cvtColor(Paint, Paint, COLOR_GRAY2BGR); + + HObject ho_Image; + HTuple hv_Out_Rectangle; + HTuple hv_Make; + HTuple hv_Text; + MatToHObject(sourceImg,&ho_Image); + _628_3_TopCoverMelted(ho_Image + ,&hv_Out_Rectangle,&hv_Make, + &hv_Text); + if(hv_Make.D() == 1) + { + for (int i = 0; i < hv_Out_Rectangle.Length() / 4; i++) + { + double lt_X = hv_Out_Rectangle[i * 4 + 1]; + double lt_Y = hv_Out_Rectangle[i * 4 ]; + double rd_X = hv_Out_Rectangle[i * 4 + 3]; + double rd_Y = hv_Out_Rectangle[i * 4 + 2]; + double width = rd_X - lt_X; + double height = rd_Y - lt_Y; + cv::Rect rect = cv::Rect(lt_X, lt_Y , width, height); + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 2); + } + std::string str = hv_Text.S(); + putTextHusky(Paint, utf8_to_gbk(str).c_str(), cv::Point(0, 50), cv::Scalar(0, 255, 0), 500); + } + else + putTextHusky(Paint, utf8_to_gbk("算法错误").c_str(), cv::Point(0, 50), cv::Scalar(0, 255, 0), 500); + + + QQueue defectInfoList; + + + detect_mutex->lock(); + + + + + + resMat.push_back(Paint); + QJsonArray detaillists; + //QByteArrayList resQbyte; + for (int i = 0; i < defectInfoList.size(); i++) + { + defectInfo mdefect = defectInfoList[i]; + //resQbyte.push_back(mdefect.defectName.toLocal8Bit()); //中文也可以QString::number(1.1).toUtf8() + //resQbyte.push_back(QString::number(mdefect.defectRect.x).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.y).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.width).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectRect.height).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[0]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[1]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectScalar[2]).toUtf8()); + //resQbyte.push_back(QString::number(mdefect.defectArea).toUtf8()); + QJsonObject detailObj; + detailObj.insert("defectName", "mdefect.defectName"); + detailObj.insert("defectRect.x", QString::number(mdefect.defectRect.x)); + detailObj.insert("defectRect.y", QString::number(mdefect.defectRect.y)); + detailObj.insert("defectRect.width", QString::number(mdefect.defectRect.width)); + detailObj.insert("defectRect.height", QString::number(mdefect.defectRect.height)); + detailObj.insert("defectScalar0", QString::number(mdefect.defectScalar[0])); + detailObj.insert("defectScalar1", QString::number(mdefect.defectScalar[1])); + detailObj.insert("defectScalar2", QString::number(mdefect.defectScalar[2])); + detailObj.insert("defectArea", QString::number(mdefect.defectArea)); + detailObj.insert("defectLength", QString::number(sqrt(mdefect.defectRect.width* mdefect.defectRect.width+ mdefect.defectRect.height* mdefect.defectRect.height))); + + detaillists.append(detailObj); + } + + + //使用完删除 + defectInfoList.clear(); + + //往主界面发 + //emit sendResultData(resMat, resQbyte); + //resQbyte.clear(); + + //isDirExist(resImgSaveDir + mysaveKey1 + "/"); + QString qimgsavePath = resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png"; + //存图数据入队,按需 + if (resMat.size() > 0 ) + { + createDirectoryIfNotExists(resImgSaveDir + getCurrentDate() + "//" + getCurrentHour() + "//"); + addImgToSaveStruct(resMat[0], "0", getResSaveFromat());//保存原图 + } + detect_mutex->unlock(); + + obj.insert("detaillists", detaillists); + obj.insert("qimgsavePath", qimgsavePath); + document.setObject(obj); + //resByte.clear(); + QByteArray qa = document.toJson(); + //resByte .enqueue(qa); + + + //resMat.release(); + sourceImg.release(); + + mySave.out_Mat.resize(0); + mysaveKey1.clear(); + qApp->processEvents(); + + + ////原图和结果都往主界面发? + //std::vector resMat; + //resMat.push_back(img2D); + //resMat.push_back(sourceRectangles); + //QByteArray resByte = mysaveKey.toLocal8Bit(); + //resByte.append(myDir2D.toLocal8Bit()); + //resByte.append(mySN.toLocal8Bit()); + //resByte.append(myDataTime.toLocal8Bit()); + + ////往主界面发送图片和数据 + ////emit writeData(resMat, resByte); + //resMat.resize(0); + //resByte.resize(0); + } + catch (...) + { + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + return false; + } + return true; + +} + +//存图加入队列 +void Hd_AI_algo_maJiaReRong::addImgToSaveStruct(cv::Mat img2D, QString mysaveKey, QString savePath) +{ + //存图数据加保存队列,加锁,避免数据访问冲突 + saveStruct_mutex->lock(); + saveImgStruct mySave; + mySave.out_Mat = img2D.clone(); + //img2D.copyTo(mySave.out_Mat); + mySave.saveKey = mysaveKey.toLocal8Bit().toStdString(); + mySave.savePath = savePath.toLocal8Bit().toStdString(); + saveStruct.enqueue(mySave); + saveStruct_mutex->unlock(); +} +void Hd_AI_algo_maJiaReRong::saveImgThread() +{ + while (1) + { + if (!ifdetectThread)return; + _sleep(5); + qApp->processEvents(); + if (saveStruct.size() < 1)continue; + for (int i = 0; i < saveStruct.size(); i++) + { + clock_t start, stop; + start = clock(); + saveStruct_mutex->lock(); + saveImgStruct mysaveStruct = saveStruct.dequeue(); + saveStruct_mutex->unlock(); + stop = clock(); + if(stop-start >1000) + std::cout << "lock stop-start >1000::************************" << stop - start << std::endl; + start = clock(); + if (mysaveStruct.out_Mat.data) + imwrite(mysaveStruct.savePath, mysaveStruct.out_Mat); + //std::cout << "savePath::" << mysaveStruct.savePath << std::endl; + stop = clock(); + if (stop - start > 1000) + std::cout << "imwrite stop-start >1000::--------------------------"<< stop - start << std::endl; + _sleep(1); + } + } +} +//检测线程,应当只运行一次 +void Hd_AI_algo_maJiaReRong::detectThread() +{ + do + { + if (!ifdetectThread)return; + + //此两行代码,避免程序卡死;_sleep的时间根据实际项目调整 + _sleep(2); + qApp->processEvents(); + //取数据加锁,避免数据访问冲突 + detect_mutex->lock(); + //if (HikMat.size() < 1 || saveKey.size() < 1 || Dir2D.size() < 1 || SN.size() < 1 )//|| DateTime1.size() < 1) + //{ + // detect_mutex->unlock(); + // continue; + //} + + cv::Mat sourceImg1 = HikMat[0];// .dequeue(); + QString mysaveKey = "saveKey";// .dequeue(); + QString myDir2D = "Dir2D";// .dequeue(); + QString mySN = "SN";// .dequeue(); + QString myDataTime = "DateTime1"; + detect_mutex->unlock(); + + //判断存图文件夹是否存在,不存在则创建 + { + //原图总文件夹 + isDirExist(oriImgSaveDir + mysaveKey); + //原图class总文件夹 + isDirExist(classImgSaveDir + mysaveKey); + //结果图总文件夹 + isDirExist(resImgSaveDir + mysaveKey); + + } + + //测试相机的, + // while (sourceImg1.cols > 2000)cv::resize(sourceImg1, sourceImg1, cv::Size(), 0.5, 0.5); + + //存图数据入队 + //addImgToSaveStruct(sourceImg1, mysaveKey, oriImgSaveDir + mysaveKey + "/" + mySN + myDataTime + ".bmp"); + + //-------检测流程,根据实际项目修改--------- + AIdetect(sourceImg1, mysaveKey, oriImgSaveDir, mySN, myDataTime); + //sourceImg1.release(); + + mysaveKey.clear(); + myDir2D.clear(); + mySN.clear(); + myDataTime.clear(); + qApp->processEvents(); + + } while (ifThreadRunDetect); + +} + +static int runNum = 0; +bool Hd_AI_algo_maJiaReRong::run() +{ + clock_t start, stop; + start = clock(); + + //Sleep(1000); + detectThread(); + stop = clock(); + qDebug() << __FUNCTION__ << "line:" << __LINE__ << " run start ::runNum::" << runNum++<<" cutTime::"< +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "imgproc/imgproc_c.h" +#include "base.h" +#include "handleAI.h" +#pragma execution_character_set("utf-8") + + + +//保存图片的结构体 +struct saveImgStruct; +//{ +// cv::Mat out_Mat; +// std::string saveKey; //关键字,用于LOG日志 +// std::string savePath; //保存路径 +//}; + +//初始化流程(一次):Hd_AI_algo_maJiaReRong,initParas +//运行流程:writeData主线程把数据传过来,run,readData此线程把数据传主线程 +class Hd_AI_algo_maJiaReRong :public Base +{ + Q_OBJECT +public: + Hd_AI_algo_maJiaReRong(); + virtual ~Hd_AI_algo_maJiaReRong(); + //从类中读数据到实例对象 + bool readData(std::vector&,QByteArray&); + //实例对象把数据写入到类 + bool writeData(std::vector&, QByteArray&); + bool initParas(QByteArray&); + //运行(Logic_taskthread.cpp) + bool run(); + //线程状态 + bool checkStatus();// { return true; }; + + bool encodeResultValue(QVector append_value,QByteArray &resultByte); + bool decodeAiValue(QByteArray byte,QVector &vec_ret); + //---------检测流程,根据实际项目修改--------- + bool AIdetect(cv::Mat img2D, QString saveKey, QString Dir2D, QString SN, QString DataTime); + //handdleAI::pSegmentation* seg_hanHouDingWei; +private: + int allNum = 0; + int StationNo=0; + int img2D = 2; + + bool moduleStatus = true; + + //输入图片队列 + QQueue HikMat; + //输入关键字队列 + QString saveKey="saveKey"; + //结果路径文件夹队列 + QString Dir2D="Dir2D"; + //产品SN队列 + QString SN="SN"; + //产品时间戳队列 + QString DateTime1="DateTime1"; + + int paramNum; + + //是否使用线程检测方式 + bool ifThreadRunDetect = false; + //检测结果图 + std::vector resMat; + //检测结果 + bool resIfNg = false; + + QQueue resByte; + + //检测线程 + void detectThread(); + //存图线程 + void saveImgThread(); + + //保存图片队列 + QQueue saveStruct; + + //判断文件夹是否存在,不存在则创建 + void isDirExist(QString fullDirName); + + //存图加入队列 + void addImgToSaveStruct(cv::Mat img2D, QString saveKey, QString savePath); +}; + +extern "C" +{ + Q_DECL_EXPORT Hd_AI_algo_maJiaReRong * create(); + Q_DECL_EXPORT void destory(Hd_AI_algo_maJiaReRong * ptr); +} +//保存图片的结构体 +struct saveImgStruct +{ + cv::Mat out_Mat; + std::string saveKey; //关键字,用于LOG日志 + std::string savePath; //保存路径 +}; +#endif // Hd_AI_927_2D_module_H diff --git a/BaseModules/Hd_AI_algo_maJiaReRong/base.h b/BaseModules/Hd_AI_algo_maJiaReRong/base.h new file mode 100644 index 0000000..00bd6b2 --- /dev/null +++ b/BaseModules/Hd_AI_algo_maJiaReRong/base.h @@ -0,0 +1,69 @@ +#ifndef __BASECLASS_H__ +#define __BASECLASS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Base :public QObject +{ + Q_OBJECT +public: + bool init_flag; //是否初始化,false,true + bool status = true; //相机状态(判断相机是否断开连接) + std::string paras; //传输数据 + std::vector m_mat; //传输图片 + std::string moduleName; //模块名称 +public: + Base(){} + virtual ~Base(){} + virtual bool readData(std::vector&,QByteArray&){return true;} + virtual bool writeData(std::vector&,QByteArray&){return true;} + virtual bool initParas(QByteArray&){return true;} + virtual bool run() {return true;} + virtual bool checkStatus() { return true; } + +//signals: + //QByteArrayList,根据实际项目进行解析;因不确定每个参数的长度,固用队列的方式。每个QByteArray表示一个值,使用时先转换为QString + //参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double) + //void sendResultData(cv::Mat resMat, QJsonObject resQbj); +}; + +/* +//QJsonObject 转QByteArray +{ + QJsonDocument document; + QJsonObject obj; + obj.insert(password, "pwd"); + obj.insert(userName, "usr"); + document.setObject(obj); + QByteArray abyte = document.toJson(QJsonDocument::Compact); +} + +//QByteArray转QJsonObject +{ + QByteArray ba = reply->readAll(); + QJsonDocument doc = QJsonDocument::fromJson(ba); + QJsonObject obj = doc.object(); +} +*/ +/* +//从类中读数据到实例对象 +virtual bool readData(std::vector&,QByteArray&){return true;} + + + + + + + + +*/ + +#endif diff --git a/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.cpp b/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.cpp index 24a420c..ab2d8ac 100644 --- a/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.cpp +++ b/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.cpp @@ -18,10 +18,10 @@ void HSnData::addSnVector(QVector snVec) QVector vec_str = queue_vector_sn.last(); if (!vec_str.isEmpty()) { - if (!vec_str[0].contains(snVec[0])) //demo流程重入检查也可以 - { - queue_vector_sn.append(snVec); - } + //if (!vec_str[0].contains(snVec[0])) //demo流程重入检查也可以 + //{ + // queue_vector_sn.append(snVec); + //} /*else std::cout << "HSnData::addSnVector err" << std::endl;*/ } diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fdecb9..2b14a71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,8 +127,8 @@ if(ON) else() SET( ${child}_target_link "opencv_world410" ) endif() - elseif(${child} MATCHES "3DAI") - SET( ${child}_target_link "cosmeticAlg" ) + elseif(${child} MATCHES "halconAlgo") + SET( ${child}_target_link "Inspection" ) elseif(${child} MATCHES "basler") SET( ${child}_target_link "GCBase_MD_VC141_v3_1_Basler_pylon" diff --git a/env/halconAlgo/dll/Inspection.dll b/env/halconAlgo/dll/Inspection.dll new file mode 100644 index 0000000000000000000000000000000000000000..f31b795a0323c3cd565c6d31539cef3a809b4fd8 GIT binary patch literal 435712 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%0DCKW6-59ODR~tq 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 OpSTy`&%ZzM8u))mF;lAm literal 0 HcmV?d00001 diff --git a/env/halconAlgo/include/algo.h b/env/halconAlgo/include/algo.h new file mode 100644 index 0000000..58b7102 --- /dev/null +++ b/env/halconAlgo/include/algo.h @@ -0,0 +1,375 @@ +#pragma once +#include +#include +#include + +//#include +//#include + +using namespace HalconCpp; + +#if 1 ºó¶˵÷Óú¯Êý + +/* + ho_Image:ÊäÈëԭͼ + hv_out_Value£ºÊä³ö¼ì²â½á¹û 1ΪÂÁ£¬2Ϊͭ + hv_Make£ºË㷨ִÐÐ״̬-1ΪË㷨NG£¬1ΪË㷨ok + ¼«ÐԼì²⺯Êý + Ԥº¸ÈýЭ»ų́ +*/ +extern "C" __declspec(dllexport) void _stdcall _628_1_Polarity_detection(HObject ho_Image, HTuple* hv_out_Value, HTuple* hv_Make); + +/* +* ho_Image£ºÊäÈëͼƬ£¨ԭͼ£© +* hv_NinePointDocument£º¾ŵãÎļþµÄ·¾¶ +* hv_SetNumber:ÉèÖýº¶¤µÄ×îС¸öÊý +* hv_Circle_Point£ºÊä³ö·Ŷ¤¿׵ÄԲÐÄx£¬ԲÐÄy£¬°뾶¡£Êý×éÐÎʽ +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +*hv_Out_Roboot1£ºÎü×ì1-> Îü×ì±àºÅ,x¾ø¶Ô×ø±꣬y¾ø¶Ô×ø±ê +* hv_Out_Roboot2£ºÎü×ì2->Îü×ì±àºţ¬x¾ø¶Ô×ø±꣬y¾ø¶Ô×ø±ê +* hv_Out_Roboot3£ºÎü×ì3->Îü×ì±àºţ¬x¾ø¶Ô×ø±꣬y¾ø¶Ô×ø±ê +* »úÆ÷ÈËÒýµ¼º¯Êý +* ÈýЭ»ų́ +*/ +extern "C" __declspec(dllexport) void _stdcall _628_BootTheMachine(HObject ho_Image, HTuple hv_NinePointDocument, HTuple hv_SetNumber, + HTuple* hv_Circle_Point, HTuple* hv_Make, HTuple* hv_Out_Roboot1, HTuple* hv_Out_Roboot2,HTuple* hv_Out_Roboot3, HTuple* hv_StartRun); + +/* +*ho_Image£ºÊäÈëͼƬ£¨ԭͼ£© +* hv_Pix£ºÏñËؾ«¶È +* hv_out_line:Êä³öֱÏß×ø±ê +* hv_distance£ºÊä³ö²âÁ¿ֵ ->¸ߣ¬¿í¡£Êý×éÐÎʽ +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +* ͭ¼«¶ú²üô²âÁ¿º¯Êý +* ÈýЭԤº¸»ų́ +*/ + +extern "C" __declspec(dllexport) void _stdcall cs_628_jier_T(HObject ho_Image, HTuple hv_Pix, HTuple* hv_out_line, HTuple* hv_out_Soldering_line, + HTuple* hv_distance, HTuple* hv_Make); + +/* +*ho_Image£ºÊäÈëͼƬ£¨ԭͼ£© +* hv_Pix£ºÏñËؾ«¶È +* hv_out_line:Êä³öֱÏß×ø±ê +* hv_distance£ºÊä³ö²âÁ¿ֵ ->¸ߣ¬¿í¡£Êý×éÐÎʽ +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +* ÂÁ¼«¶ú²üô²âÁ¿º¯Êý +* ÈýЭԤº¸»ų́ +*/ + +extern "C" __declspec(dllexport) void _stdcall cs_628_jier_L(HObject ho_Image, HTuple hv_Pix, HTuple* hv_out_line, HTuple* hv_distance, + HTuple* hv_Make); + + + + + + + +/* +* ho_Image£ºÊäÈëͼƬ£¨ԭͼ£© +* hv_model£º¸³ֵΪ1ʱÊǻù׼±궨ģʽ£¬0ΪÕý³£¼ì²âģʽ +* hv_BenchmarkFile£º»ù׼±궨ÎļþµÄ·¾¶ +* hv_NinePointDocument£º¾ŵãÎļþµÄ·¾¶ +*hv_CameraNumber£ºÉ϶¤Ïà»ú±àºÅ +* hv_out_Cirle£ºÊä³ö·Ŷ¤¿׵ÄԲÐÄx£¬ԲÐÄy£¬°뾶¡£Êý×éÐÎʽ +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐУ¬Êä³öΪ2Ϊ»ù׼±궨³ɹ¦ +* hv_offset_x£ºxÖ᷽ÏòÉÏÏà¶ÔÓڻù׼λÖõÄƫ²î +* hv_offset_y£ºyÖ᷽ÏòÉÏÏà¶ÔÓڻù׼λÖõÄƫ²î +* ´򶤶¤Òýµ¼º¯Êý +* ÈýЭµĻų́ +*/ + +extern "C" __declspec(dllexport) void _stdcall _628_NailingPosition(HObject ho_Image, HTuple hv_model, HTuple hv_BenchmarkFile, + HTuple hv_NinePointDocument, HTuple hv_CameraNumber, HTuple* hv_out_Cirle, HTuple* hv_Make, + HTuple* hv_offset_x, HTuple* hv_offset_y); + + +/* + ho_Image:ÊäÈëԭͼ + hv_out_Value£ºÊä³ö¼ì²â½á¹û[1,1] .1ΪÂÁ£¬[2,2] 2Ϊͭ + hv_Make£ºË㷨ִÐÐ״̬-1ΪË㷨NG£¬1ΪË㷨ok + ¼«ÐԼì²⺯Êý + Öպ¸ÈýЭ»ų́ +*/ +extern "C" __declspec(dllexport) void _stdcall _628_2_Polarity_detection(HObject ho_Image, HTuple* hv_out_Value, HTuple* hv_Make); + +/* +* ho_Image_L£ºÊäÈëͼƬÂÁ +* ho_Image_T£ºÊäÈëͼƬͭ +* hv_Pix_T:ͭ¼«ÏñËؾ«¶È +* hv_Pix_L:ÂÁ¼«ÏñËؾ«¶È +* hv_Out_Vale_L£ºÊä³öÂÁ¼«²âÁ¿ֵ°üÀ¨Á½¸öֵ +* hv_Out_Line_L£ºÊä³öÂÁ¼«»®Ïß×ø±ê +* hv_Out_Vale_T£ºÊä³öÂÁ¼«²âÁ¿ֵ°üÀ¨Á½¸öֵ +* hv_Out_Line_T£ºÊä³öͭ¼«¼«»®Ïß×ø±ê +* hv_Out_extremum_Value£ºÊä³öÁ½¼«֮¼äµľàÀë +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +* Ԥº¸º¸ǰ²âÁ¿º¯Êý +* ÈýЭµĻų́ +*/ +extern "C" __declspec(dllexport) void _stdcall _628_2_BeforeWelding_Distnce(HObject ho_Image_L, HObject ho_Image_T, HTuple hv_Pix_T, + HTuple hv_Pix_L, HTuple* hv_Out_Vale_L, HTuple* hv_Out_Line_L, HTuple* hv_Out_Vale_T, + HTuple* hv_Out_Line_T, HTuple* hv_Out_extremum_Value, HTuple* hv_Makes); + +/* +* ho_Image £ºÊäÈëͼƬÂÁ¼« +* ho_ImageSeg £ºÊäÈëͼƬsegͼ£¨¶þֵͼ£© +* hv_Pix : ÂÁ¼«ÏñËؾ«¶È +* hv_Out_TopLeft_Line : ×óÉÏÊä³öÏߵĶ˵ã×ø±ê +* hv_Out_TopLeft_Value £º×óÉÏÊä³öµIJâÁ¿ֵ£¨2¸öֵ£© +* hv_Out_TopRight_Line £ºÓÒÉÏÊä³öÏߵĶ˵ã×ø±ê +* hv_Out_TopRight_Value £ºÓÒÉÏÊä³öµIJâÁ¿ֵ£¨2¸öֵ£© +* hv_Out_BottomLeft_Line £º×óÏÂÊä³öÏߵĶ˵ã×ø±ê +* hv_Out_BottomLeft_Value £º×óÏÂÊä³öµIJâÁ¿ֵ£¨2¸öֵ£© +** hv_Out_BottomRight_Line £ºÓÒÏÂÊä³öÏߵĶ˵ã×ø±ê +* hv_Out_BottomRight_Value £ºÓÒÏÂÊä³öµIJâÁ¿ֵ£¨2¸öֵ£© +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +* Ԥº¸º¸ºó²âÁ¿ÂÁ¼«º¯Êý +* ÈýЭµĻų́ +*/ + +extern "C" __declspec(dllexport) void _stdcall _628_AfterWelding_Distance_L(HObject ho_Image, HObject ho_ImageSeg, HTuple hv_Pix, + HTuple* hv_Out_TopLeft_Line, HTuple* hv_Out_TopLeft_Value, HTuple* hv_Out_TopRight_Line, + HTuple* hv_Out_TopRight_Value, HTuple* hv_Out_BottomLeft_Line, HTuple* hv_Out_BottomLeft_Value, + HTuple* hv_Out_BottomRight_Line, HTuple* hv_Out_BottomRight_Value, HTuple* hv_Make); + +/* +* ho_Image £ºÊäÈëͼƬÂÁ¼« +* ho_ImageSeg £ºÊäÈëͼƬsegͼ£¨¶þֵͼ£© +* hv_Pix : ÂÁ¼«ÏñËؾ«¶È +* hv_Out_TopLeft_Line : ×óÉÏÊä³öÏߵĶ˵ã×ø±ê +* hv_Out_TopLeft_Value £º×óÉÏÊä³öµIJâÁ¿ֵ£¨2¸öֵ£© +* hv_Out_TopRight_Line £ºÓÒÉÏÊä³öÏߵĶ˵ã×ø±ê +* hv_Out_TopRight_Value £ºÓÒÉÏÊä³öµIJâÁ¿ֵ£¨2¸öֵ£© +* hv_Out_BottomLeft_Line £º×óÏÂÊä³öÏߵĶ˵ã×ø±ê +* hv_Out_BottomLeft_Value £º×óÏÂÊä³öµIJâÁ¿ֵ£¨2¸öֵ£© +** hv_Out_BottomRight_Line £ºÓÒÏÂÊä³öÏߵĶ˵ã×ø±ê +* hv_Out_BottomRight_Value £ºÓÒÏÂÊä³öµIJâÁ¿ֵ£¨2¸öֵ£© +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +* Ԥº¸º¸ºó²âÁ¿ͭ¼«º¯Êý +* ÈýЭµĻų́ +*/ + +extern "C" __declspec(dllexport) void _stdcall _628_AfterWelding_Distance_T(HObject ho_Image, HObject ho_ImageSeg, HTuple hv_Pix, + HTuple* hv_Out_TopLeft_Line, HTuple* hv_Out_TopLeft_Value, HTuple* hv_Out_TopRight_Line, + HTuple* hv_Out_TopRight_Value, HTuple* hv_Out_BottomLeft_Line, HTuple* hv_Out_BottomLeft_Value, + HTuple* hv_Out_BottomRight_Line, HTuple* hv_Out_BottomRight_Value, HTuple* hv_Make); + +/* +* ho_Image £ºÊäÈëͼƬ +* hv_Out_Rectangle : Êä³ö¾ØÐεÄÁ½¸ö¶¥µã×ø±ê +* hv_Text £ºÊä³ö¼ì²âÐÅϢ×ַû´®¸ñʽ +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +*ÈíÁ¬½ÓÈÈÈ۹¤λº¯Êý +* ÈýЭµĻų́ +*/ +extern "C" __declspec(dllexport) void _stdcall _628_3_TopCoverMelted(HObject ho_Image, HTuple* hv_Out_Rectangle, HTuple* hv_Make, + HTuple* hv_Text); + +/* +* ho_Image_L £ºÊäÈëͼƬ£¨ÂÁ£© +* ho_Image_T £ºÊäÈëͼƬ£¨ͭ£© +* hv_Pix_L £ºÂÁ¼«Ïà»úÏñËؾ«¶È +* hv_Pix_T £ºͭ¼«Ïà»úÏñËؾ«¶È +* hv_Out_Line_T £ºͭ¼«Êä³öÏߵĶ˵ã×ø±꣨ÐèÅжϸöÊý£© +* hv_Out_Point_T £ºͭ¼«Êä³öÖÐÐÄԲµÄÖÐÐÄ×ø±꼰°뾶 +* hv_Out_Chamfering_T £ºͭ¼«Êä³öµ¹½Çֵ +* hv_Distances_T £ºͭ¼«Êä³öÉÏÏ£¬×óÓҾàÀë +* hv_Out_Line_L £ºÂÁ¼«Êä³öÏߵĶ˵ã×ø±꣨ÐèÅжϸöÊý£© +* hv_Out_Point_L £ºÂÁ¼«Êä³öÖÐÐÄԲµÄÖÐÐÄ×ø±꼰°뾶 +* hv_Out_Chamfering_L £ºÂÁ¼«Êä³öµ¹½Çֵ +* hv_Distances_L £ºÂÁ¼«Êä³öÉÏÏ£¬×óÓҾàÀë +* hv_Out_InterpoleValue £ºÊä³öÁ½¼«Öù֮¼ä¾àÀë +* hv_Makes£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +*ÈíÁ¬½Ӽ«Öù²âÁ¿¹¤λº¯Êý +* ÈýЭµĻų́ +*/ + +extern "C" __declspec(dllexport) void _stdcall _628_3_PoleDetections(HObject ho_Image_L, HObject ho_Image_T, HTuple hv_Pix_L, + HTuple hv_Pix_T, HTuple* hv_Out_Line_T, HTuple* hv_Out_Point_T, HTuple* hv_Out_Chamfering_T, + HTuple* hv_Distances_T, HTuple* hv_Out_Line_L, HTuple* hv_Out_Point_L, HTuple* hv_Out_Chamfering_L, + HTuple* hv_Distances_L, HTuple* hv_Out_InterpoleValue, HTuple* hv_Makes); + +/* +* ho_Image £ºÊäÈëͼƬ +* hv_Pix £ºÏà»úÏñËؾ«¶È +* hv_Distance_Value £º¼ä¾à²âÁ¿ֵ +* hv_Out_Point £ºÊä³ö·À±¬·§ÖÐÐĵãºͶþάÂëÖÐÐĵã¾àÀë +* hv_Makes£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐÐ +*ÈíÁ¬½ӷÀ±¬·§Óë¶þάÂë֮¼ä¾àÀë²âÁ¿º¯Êý +* ÈýЭµĻų́ +*/ +extern "C" __declspec(dllexport) void _stdcall _628_3_ExplosionProofValves(HObject ho_Image, HTuple hv_Pix, HTuple* hv_Distance_Value, + HTuple* hv_Out_Point, HTuple* hv_Make); + + + + + + + + + + + + + + +/* +* ho_Image£ºÊäÈëͼƬ£¨ԭͼ£© +* hv_model£º¸³ֵΪ1ʱÊǻù׼±궨ģʽ£¬0ΪÕý³£¼ì²âģʽ +* hv_BenchmarkFile£º»ù׼±궨ÎļþµÄ·¾¶ +* hv_NinePointDocument£º¾ŵãÎļþµÄ·¾¶ +*hv_CameraNumber£ºÉ϶¤Ïà»ú±àºÅ +* hv_out_Cirle£ºÊä³ö·Ŷ¤¿׵ÄԲÐÄx£¬ԲÐÄy£¬°뾶¡£Êý×éÐÎʽ +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐУ¬Êä³öΪ2Ϊ»ù׼±궨³ɹ¦ +* hv_offset_x£ºxÖ᷽ÏòÉÏÏà¶ÔÓڻù׼λÖõÄƫ²î +* hv_offset_y£ºyÖ᷽ÏòÉÏÏà¶ÔÓڻù׼λÖõÄƫ²î +* É϶¤Òýµ¼º¯Êý +* ӮºϵĻų́ +*/ + +extern "C" __declspec(dllexport) void _stdcall _628_SealingNails(HObject ho_Image, HTuple hv_model, HTuple hv_BenchmarkFile, + HTuple hv_NinePointDocument, HTuple hv_CameraNumber, HTuple* hv_out_Cirle, HTuple* hv_Make, + HTuple* hv_offset_x, HTuple* hv_offset_y); + +/* +* ho_Image£ºÊäÈëͼƬ£¨ԭͼ£© +* hv_model£º¸³ֵΪ1ʱÊǻù׼±궨ģʽ£¬0ΪÕý³£¼ì²âģʽ +* hv_BenchmarkFile£º»ù׼±궨ÎļþµÄ·¾¶ +* hv_NinePointDocument£º¾ŵãÎļþµÄ·¾¶ +*hv_CameraNumber£ºԤº¸Ïà»ú±àºÅ +* hv_out_Cirle£ºÊä³ö·Ŷ¤¿׵ÄԲÐÄx£¬ԲÐÄy£¬°뾶¡£Êý×éÐÎʽ +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐУ¬Êä³öΪ2Ϊ»ù׼±궨³ɹ¦ +* hv_offset_x£ºxÖ᷽ÏòÉÏÏà¶ÔÓڻù׼λÖõÄƫ²î +* hv_offset_y£ºyÖ᷽ÏòÉÏÏà¶ÔÓڻù׼λÖõÄƫ²î +* hv_Text:Êä³ö±¨¾¯ÐÅϢ°üÀ¨£¨ok£¬Î޶¤£¬Ç̶¤£¬·´¶¤£© +* Ӯºϻų́ +* Ԥº¸º¯Êý +*/ +extern "C" __declspec(dllexport) void _stdcall _628_yuhan(HObject ho_Image, HTuple hv_Model, HTuple hv_BenchmarkFile, HTuple hv_NinePointDocument, + HTuple hv_CameraNumber, HTuple* hv_out_Cirle, HTuple* hv_Make, HTuple* hv_offset_x, + HTuple* hv_offset_y, HTuple* hv_Text); + +/* +* ho_Image£ºÊäÈëͼƬ£¨ԭͼ£© +* hv_model£º¸³ֵΪ1ʱÊǻù׼±궨ģʽ£¬0ΪÕý³£¼ì²âģʽ +* hv_BenchmarkFile£º»ù׼±궨ÎļþµÄ·¾¶ +* hv_NinePointDocument£º¾ŵãÎļþµÄ·¾¶ +*hv_CameraNumber£ºԤº¸Ïà»ú±àºÅ +* hv_out_Cirle£ºÊä³ö·Ŷ¤¿׵ÄԲÐÄx£¬ԲÐÄy£¬°뾶¡£Êý×éÐÎʽ +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐУ¬Êä³öΪ2Ϊ»ù׼±궨³ɹ¦ +* hv_offset_x£ºxÖ᷽ÏòÉÏÏà¶ÔÓڻù׼λÖõÄƫ²î +* hv_offset_y£ºyÖ᷽ÏòÉÏÏà¶ÔÓڻù׼λÖõÄƫ²î +* Öպ¸º¯Êý +* ӮºϵĻų́ +*/ +extern "C" __declspec(dllexport) void _stdcall _628_FinalWelding(HObject ho_Image, HTuple hv_model, HTuple hv_BenchmarkFile, + HTuple hv_NinePointDocument, HTuple hv_CameraNumber, HTuple* hv_out_Cirle, HTuple* hv_Make, + HTuple* hv_offset_x, HTuple* hv_offset_y); + +/* +* ho_Image1£ºÊäÈëͼƬ£¨ԭͼ£© +* ho_Image2£ºÊäÈëͼƬ£¨ԭͼ£© +* ho_Image3£ºÊäÈëͼƬ£¨ԭͼ£© +* ho_Image4£ºÊäÈëͼƬ£¨ԭͼ£© +*ho_Out_Image1£ºÊä³öͼƬ1 +* ho_Out_image2£ºÊä³öͼƬ2 +* hv_Make£ºÊä³ö-1ΪË㷨´íÎó£¬Êä³ö1ΪË㷨Õý³£ִÐУ¬Êä³öΪ2Ϊ»ù׼±궨³ɹ¦ +* hv_sacats£º¹âԴÓë¹âÖá¼нÇ +* hv_Tilts£º¹âԴ°²װλÖà +*/ + +extern "C" __declspec(dllexport) void _stdcall _628_25D(HObject ho_Image1, HObject ho_Image2, HObject ho_Image3, HObject ho_Image4, + HObject* ho_Out_Image1, HObject* ho_Out_image2, HTuple hv_sacats, HTuple hv_Tilts, + HTuple* hv_Make); + +//Function Name: Cal_Defect_Height +//Author: +//Description: 2D»ñȡȱÏݺó, »ùÓÚÌØÕ÷µãµÄλÖùØϵ, ½¨Á¢2DºÍ3DµÄӳÉ乨ϵ. +//Input: in_Image2: ÃܷⶤÇøÓòµÄ2D¶þֵ»¯µÄͼÏñ(bmp),, +// in_Image3: 3DÉî¶ÈͼÏñ(tiff), +// in_xResolution: xÏ򾫶È(mm/pix) +// in_yResolution: yÏ򾫶È(mm/pix), +// in_zResolution: ZÏ򾫶È(mm/pix) +// in_DefectLocation: 2dͼÏñȱÏÝ×ø±êµãλÖÃ, ÿËĸöһ×é, +// in_DefectLocation[0]:ȱÏÝÍâ½ӾØÐεÄXÏò×ø±ê(pix) +// in_DefectLocation[1]:ȱÏÝÍâ½ӾØÐεÄyÏò×ø±ê(pix) +// in_DefectLocation[2]:ȱÏÝÍâ½ӾØÐεĿí¶Èwidth(pix) +// in_DefectLocation[3]:ȱÏÝÍâ½ӾØÐεĸ߶Èheight(pix) +// in_FilePath, ¾ØÕómartixÎļþ·¾¶, +// in_offsetValue, λÖò¹³¥ֵ, +// in_offsetValue[0]: ×ó¹¤λXÏ򲹳¥ֵ(pix) +// in_offsetValue[1]: ×ó¹¤λYÏ򲹳¥ֵ(pix) +// in_offsetValue[2]: Óҹ¤λXÏ򲹳¥ֵ(pix) +// in_offsetValue[3]: Óҹ¤λYÏ򲹳¥ֵ(pix) +// in_HoffsetValue, ȱÏݸ߶Ȳ¹³¥ֵ(Õýֵ,µ¥λ:mm) +//output£º out_Height: ȱÏݵĸ߶È(mm), +// out_Flag: Ë㷨ִÐеıê־λ, 0: Ë㷨ִÐÐÒ쳣 1:Ë㷨ִÐÐok +// Ӯºϻų́ +//verion change log: XXX 2024-10-22 V1.1 Create new function + +extern "C" __declspec(dllexport) void _stdcall Cal_Defect_Height(HObject ho_in_Image2, HObject ho_in_Image3, HTuple hv_in_xResolution, + HTuple hv_in_yResolution, HTuple hv_in_zResolution, HTuple hv_in_DefectLocation, + HTuple hv_in_FilePath, HTuple hv_in_OffsetValue, HTuple hv_in_HoffsetValue, HTuple* hv_out_Height, + HTuple* hv_out_Angle, HTuple* hv_out_Flag); + + + + + +#endif + +#if 1 halconµ÷Óú¯Êý + +void gen_arrow_contour_xld(HObject* ho_Arrow, HTuple hv_Row1, HTuple hv_Column1, + HTuple hv_Row2, HTuple hv_Column2, HTuple hv_HeadLength, HTuple hv_HeadWidth); +void Get_Rectangle2_Corner_new(HObject ho_Rectangle, HTuple* hv_out_Row, HTuple* hv_out_Col); + +void pts_to_best_line(HObject* ho_Line, HTuple hv_Rows, HTuple hv_Cols, HTuple hv_ActiveNum, + HTuple* hv_Row1, HTuple* hv_Column1, HTuple* hv_Row2, HTuple* hv_Column2); +void rake(HObject ho_Image, HObject* ho_Regions, HTuple hv_Elements, HTuple hv_DetectHeight, + HTuple hv_DetectWidth, HTuple hv_Sigma, HTuple hv_Threshold, HTuple hv_Transition, + HTuple hv_Select, HTuple hv_Row1, HTuple hv_Column1, HTuple hv_Row2, HTuple hv_Column2, + HTuple* hv_ResultRow, HTuple* hv_ResultColumn); + +void pts_to_best_circle(HObject* ho_Circle, HTuple hv_Rows, HTuple hv_Cols, HTuple hv_ArcType, + HTuple hv_ActiveNum, HTuple* hv_RowCenter, HTuple* hv_ColCenter, HTuple* hv_Radius, + HTuple* hv_StartPhi, HTuple* hv_EndPhi, HTuple* hv_PointOrder, HTuple* hv_ArcAngle); + +void region_to_spoke(HObject ho_Region, HTuple hv_Offset, HTuple* hv_SpokeRows, + HTuple* hv_SpokeCols); + +void spoke(HObject ho_Image, HObject* ho_Regions, HTuple hv_Elements, HTuple hv_DetectHeight, + HTuple hv_DetectWidth, HTuple hv_Sigma, HTuple hv_Threshold, HTuple hv_Transition, + HTuple hv_Select, HTuple hv_ROIRows, HTuple hv_ROICols, HTuple hv_Direct, HTuple* hv_ResultRow, + HTuple* hv_ResultColumn, HTuple* hv_ArcType); + +void _628_PreWelded(HObject ho_Image, HTuple hv_model, HTuple hv_BenchmarkFile, + HTuple hv_NinePointDocument, HTuple hv_CameraNumber, HTuple* hv_out_Cirle, HTuple* hv_Make, + HTuple* hv_offset_x, HTuple* hv_offset_y); + +void BeforeWelding_L(HObject ho_Image_L, HTuple hv_Pix_T, HTuple* hv_Make, HTuple* hv_Out_Line, + HTuple* hv_Out_Vale, HTuple* hv_Out_PointRow, HTuple* hv_Out_PointCol); + +void BeforeWelding_T(HObject ho_Image_T, HTuple hv_Pix_T, HTuple* hv_Make, HTuple* hv_Out_Line, + HTuple* hv_Out_Vale, HTuple* hv_Out_PointRow, HTuple* hv_Out_PointCol); + + +void Cal_PointCould_HeightValue(HObject ho_Tmp_Region, HObject ho_Zero_Region, HObject ho_XImageScaled, + HObject ho_YImageScaled, HObject ho_ZImageScaled, HTuple hv_HeightPointPerenct, + HTuple* hv_Out_Mean, HTuple* hv_Out_Max, HTuple* hv_Out_Min, HTuple* hv_Out_Height, + HTuple* hv_Out_Flag, HTuple* hv_Out_NgTails); + +void cs_628_PoleDetection(HObject ho_Image, HTuple hv_Pix, HTuple* hv_Distances, + HTuple* hv_UP_Down_Point, HTuple* hv_life_Ren_Point, HTuple* hv_Point, HTuple* hv_Chamfering, + HTuple* hv_Make); + + +#endif + + + + + diff --git a/env/halconAlgo/include/unionCal.h b/env/halconAlgo/include/unionCal.h new file mode 100644 index 0000000..911c788 --- /dev/null +++ b/env/halconAlgo/include/unionCal.h @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include "algo.h" +class unionCalculate +{ +public: + //struct dataStruct + //{ + // cv::Mat matImg; + // HObject halconImg; + // dataStruct(cv::Mat img1, HObject img2) + // :matImg(img1), + // halconImg(img2) + // { + + // } + //}; + //方法1 用sn匹配 + //std::unordered_map<,std::string,dataStruct> dataMap; + //void insertSubData(std::string& sign,cv::Mat& img) + //{ + // dataVec.insert(std::make_pair(sign, img)); + //} + //dataStruct getData(std::string& sign) + //{ + //} + + //方法2 获取最新的 + //子算法转完hobject再发送 + + + void insertSubData(HObject img) + { + mutexUnionCaculate.lock(); + dataQueue.enqueue(img); + mutexUnionCaculate.unlock(); + } + bool getData(HObject& res) + { + mutexUnionCaculate.lock(); + if (dataQueue.size() == 0) + { + mutexUnionCaculate.unlock(); + return false; + } + res = dataQueue.dequeue(); + dataQueue.clear(); + mutexUnionCaculate.unlock(); + return true; + } + + struct calResult + { + bool algoFailed = false; + std::vectorlines; + std::vectorpoints; + double upDownDistance, leftRightDistance; + double interpoleValue; + }; + void insertResult(calResult res) + { + mutexUnionCaculate.lock(); + resultQueue.enqueue(res); + mutexUnionCaculate.unlock(); + } + bool getResult(calResult& res) + { + mutexUnionCaculate.lock(); + if (resultQueue.size() == 0) + { + mutexUnionCaculate.unlock(); + return false; + } + res = resultQueue.dequeue(); + resultQueue.clear(); + mutexUnionCaculate.unlock(); + return true; + } +private: + QQueue dataQueue; + QQueueresultQueue; + std::mutex mutexUnionCaculate; +}; + + +#pragma once diff --git a/env/halconAlgo/lib/Inspection.lib b/env/halconAlgo/lib/Inspection.lib new file mode 100644 index 0000000000000000000000000000000000000000..5a0e12a084d4654f08f7114cbe13ebf93836d5ca GIT binary patch literal 5442 zcmcIoO=ufe5FW{P>?pP*OKu3IrE=@032hx&mRtwYBHK!diR2htaeE1yt+cW>C|V<&CV_}EYjN2ly+GurIdoMWJM&qj4UQ{x>9;eDP+~+qLfg}x?IdCQrc=*SEasv zvbjB~kbOj&RB~ELu`U5uV70HSV%Vb}Hjr7DxP1DnPB_(I_YEjslT|(L;6!iu>a+FWW1u3EAFj1sain^HB z+14ERgz=_Ylncz|Rwsyj(p>pcLE zu(llT1;`;|%LkD318gE)>jxM|y6Xj4Kz<3wgF%2>s22_ZJnjQnMcTmoZRD>ZVTpo% z@IxNKIn%1&;vdQKpzZ15M1DHqR|OnAnJfR{&kFzk?%asc97NN^LiDIYp!HxSAkLwXX4xt-v9G}7}M3-0G)JqWbcO#n~ zPnQ>tKyW3HQZps3tmX93k-4LxCl~s2+(#!$Ex3hCgS&3&)q(X zh%T%!m6y8XJ9oFp(I`tL%U%aJKL76!BD*;Bq$!v@@bTzQGJcz4V9@!u~aau-Lg)7k76JN8;_WdD8N zy-A(=J`>y4+wKMG71!+tgxYPQ+PbmbqB>=lwxZvs?CRmD_4+&;xHxSgyBsq6%U|Mu z5%&JZ*lAlT+osL2W3k#8fA{O}H>n5kaBN#=D(6dca6x~ zOUo$=6g)o #include #define FENGZHUANG_API __declspec(dllexport) @@ -15,8 +15,8 @@ using namespace cv; FENGZHUANG_API void putTextHusky(Mat& dst, const char* str, Point org, Scalar color, int fontSize, const char* fn = "Arial", bool italic = false, bool underline = false); -FENGZHUANG_API int32_t createDirectory(const wstring& directoryPath); -FENGZHUANG_API int32_t createDirectory(const string& directoryPath); +FENGZHUANG_API int32_t createDirectory(const std::wstring& directoryPath); +FENGZHUANG_API int32_t createDirectory(const std::string& directoryPath); FENGZHUANG_API char* UnicodeToAnsi(const wchar_t* szStr); FENGZHUANG_API wchar_t* AnsiToUnicode(const char* szStr); FENGZHUANG_API std::string WString2String(const std::wstring& ws);