diff --git a/BaseModules/Hd_AI_2D3Ddata_module/CMakeLists.txt b/BaseModules/Hd_AI_2D3Ddata_module/CMakeLists.txt new file mode 100644 index 0000000..9dafdb5 --- /dev/null +++ b/BaseModules/Hd_AI_2D3Ddata_module/CMakeLists.txt @@ -0,0 +1,70 @@ +# 获取获取当前目录名 +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" "handdleAI3.0") +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) + +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 + ) + + +# 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_2D3Ddata_module/Hd_AI_2D3Ddata_module.cpp b/BaseModules/Hd_AI_2D3Ddata_module/Hd_AI_2D3Ddata_module.cpp new file mode 100644 index 0000000..6035cca --- /dev/null +++ b/BaseModules/Hd_AI_2D3Ddata_module/Hd_AI_2D3Ddata_module.cpp @@ -0,0 +1,921 @@ +#include "Hd_AI_2D3Ddata_module.h" +#include + +#include +#include + + +#pragma execution_character_set("utf-8") +using namespace cv; +using namespace std; + +//原图保存文件夹 +static QString oriImgSaveDir = "F:/ORI/"; +//原图class图保存文件夹 +static QString classImgSaveDir = "F:/ORIclass/"; +//结果保存文件夹 +static QString resImgSaveDir = "F:/res/"; + +//切图的class的width,根据项目实际设置! +static int sizeW = 150; +//切图的class的height,根据项目实际设置! +static int sizeH = 150; +//切图的class的width,根据项目实际设置!0为黑,255为白; +static int fillColour = 0; + + +//检测线程锁 +static QMutex* detect_mutex; +//保存线程锁 +static QMutex* saveStruct_mutex; +//检测线程运行标志 +static bool ifdetectThread = false; + + +// 缺陷信息结构体 +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; + + + +//判断文件是否存在,不存在则创建 +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_2D3Ddata_module::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_2D3Ddata_module* create() +{ + qDebug() << "[INFO] " << " Hd_AI_2D3Ddata_module create success!" ; + return new Hd_AI_2D3Ddata_module; +} + +void destory(Hd_AI_2D3Ddata_module* ptr) +{ + qDebug() << "[INFO] " << "Hd_AI_2D3Ddata_module destory success!"; + if(ptr) + { + delete ptr; + ptr = nullptr; + } +} + +Hd_AI_2D3Ddata_module::Hd_AI_2D3Ddata_module() +{ + detect_mutex = new QMutex; + saveStruct_mutex = new QMutex; + ifdetectThread = false; +} + +Hd_AI_2D3Ddata_module::~Hd_AI_2D3Ddata_module() +{ + 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);*/ +} + +QByteArray m_writeData; +static int defectNums = 0; +//static cv::Mat resMat; +//从类中读数据到实例对象 +bool Hd_AI_2D3Ddata_module::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()) + vec_mat.push_back(HikMat[0]); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << " resMat.data err:"; + } + //if (allNum++ % 21 == 20) + // data = "2";// resByte.dequeue(); + //else + //if (allNum++ % 2 == 0) + // data = "0*2*3*2";// resByte.dequeue(); + //else + data = "1";// "0*1*2*3";//1OK,2NG + resByte.clear(); + ////之后根据结果类修改 + //if(resIfNg == true) + // data = "NG"; + //else data = "OK"; + detect_mutex->unlock(); + return true; +} +//从类中读数据到实例对象 +bool Hd_AI_2D3Ddata_module::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; +} +//接收数据 +bool Hd_AI_2D3Ddata_module::writeData(std::vector& vec_mat, QByteArray &data) +{ + m_writeData = 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(); + HikMat.enqueue(vec_mat[0]); + //测试相机的, + //while (HikMat[0].cols > 2000)cv::resize(HikMat[0], HikMat[0], cv::Size(), 0.1, 0.1); + //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(); + + //cv::Mat resultDstImg = vec_mat[0].clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + //resultDstImg = pSeg->Evaluate(resultDstImg); + //resultDstImg.release(); + + return true; +} +//初始化 +bool Hd_AI_2D3Ddata_module::initParas(QByteArray& byte) +{ + //if(byte.isEmpty()) + //{ + // qCritical() << "[Error] " << " byte is null!"; + // return false; + //} + //QVector vec_ret; + + //decodeAiValue(byte,vec_ret); + //if(vec_ret.isEmpty()) + //{ + // qCritical() << "[Error] " << " vec_ret is null!"; + // return false; + //} + + ///*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_2D3Ddata_module::detectThread); + ////运行存图线程 + //QFuture future2 = QtConcurrent::run(this, &Hd_AI_2D3Ddata_module::saveImgThread); + ////detectThread(); + + ////原图总文件夹 + //isDirExist(oriImgSaveDir); + ////原图class总文件夹 + //isDirExist(classImgSaveDir); + ////结果图总文件夹 + //isDirExist(resImgSaveDir); + + + wstring taskPath = L"task\\haiwai_demo_model1\\haiwai_demo_model1.task"; + wcout.imbue(locale("zh_CN")); + + //bool ret = instance(taskPath, 0); + //if (!ret) + //{ + // //销毁实例 + // destroyInstance(); + // cout << "run task false\n"; + // return false; + //} + //vector srcMats; + //vector results; + ////传入图片vector 和 接收结果的容器 + //ret = PBTASK_EXPORT::run(srcMats, results);//初始化一下AI模型 + + //qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ; + return true; +} +// 解码 +bool Hd_AI_2D3Ddata_module::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_2D3Ddata_module::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(); + //} + + QQueue defectInfoList; + + //seg后class检测逻辑 + /*if (0) { + Mat sourceRectangles;// 在原图缺陷位置,边界矩形框出 + sourceRectangles = sourceImg.clone(); + //sourceImg.copyTo(sourceRectangles); + vector> contours; + + //seg检测 + Mat resultDstImg;// = sourceImg.clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + resultDstImg = pSeg->Evaluate(sourceImg); + //int resultClass = pCls->Evaluate(sourceImg); //类别id + + //opencv对二值图进行处理 + //GaussianBlur(resultDstImg, resultDstImg, Size(3, 3), 1); //缺陷过滤值较大时,可以减少检出数;缺陷较小时,漏检较多 + threshold(resultDstImg, resultDstImg, pSeg->m_classes, 255, THRESH_BINARY_INV);//大于部分设为0,小于部分设为255 + //findContours(resultDstImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); //不识别内部轮廓,有些情况抓到的图像外框? + findContours(resultDstImg, contours, cv::noArray(), cv::RETR_TREE, cv::CHAIN_APPROX_NONE); //需要内部轮廓的写法 + + if (contours.size() == 0) + { + //printf("Notlabeled\n"); + } + else + { + for (int j = 0; j < contours.size(); j++) + { + Rect rect1 = boundingRect(Mat(contours[j])); + + //最小缺陷过滤参数,根据项目实际设置! + int filterNum = 10; + float w = rect1.width * 1.0; + float h = rect1.height * 1.0; + if ((w / sourceImg.cols > 0.98) || (h / sourceImg.rows > 0.98)) + { + //抓到图像外框了 + //qDebug() << "NG is too big..."; + continue; + } + //且逻辑,有的项目需要或逻辑!! + if ((w > filterNum && h > filterNum))//||rect1.area> filterNum*filterNum*1.5) + { + //class检测mat + Mat imgCls; + + //此段为切图逻辑为: + //缺陷外扩5像素; + //判断最大边,大于class建网大小的,按照最大边尺寸比例切大图,然后resize为建网大小;最大边小于等于建网大小的,按照建网大小切图; + //越界填充 + { + int startX = rect1.x - 5; + int startY = rect1.y - 5; + int endX = rect1.x + rect1.width + 5; + int endY = rect1.y + rect1.height + 5; + + //尺寸比例 + double maxscaler, scalerX, scalerY; + maxscaler = 1.0; + scalerX = (endX - startX) * 1.0 / sizeW;//*1.0转为float,避免被取整了 + scalerY = (endY - startY) * 1.0 / sizeH; + + maxscaler = max(maxscaler, max(scalerX, scalerY)); + + int avgX = (rect1.x + rect1.x + rect1.width) / 2; //中点X位置 + int avgY = (rect1.y + rect1.y + rect1.height) / 2;//中点Y位置 + + startX = avgX - int(maxscaler * sizeW / 2);//强转为int + endX = startX + int(maxscaler * sizeW); + startY = avgY - int(maxscaler * sizeH / 2); + endY = startY + int(maxscaler * sizeH); + + //判断是否越界,越界要填充的像素个数 + int copyTop = max(0, 0 - startY); + int copyButton = max(0, endY - sourceImg.rows); + int copyLeft = max(0, 0 - startX); + int copyRight = max(0, endX - sourceImg.cols); + + //切图不要越界,会崩溃 + Mat imgCut = sourceImg(Range(max(0, startY - copyTop), min(sourceImg.rows, endY - copyButton)), + Range(max(0, startX - copyLeft), min(sourceImg.cols, endX - copyRight))); //起始行,终止行;起始列,终止列 + + //图像扩充 + if (sourceImg.channels() == 1) + { + copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, fillColour); + } + else + { + copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, Scalar(fillColour, fillColour, fillColour)); + } + //resize为建网大小 + cv::resize(imgCut, imgCls, cv::Size(sizeW, sizeH)); + imgCut.release(); + } + + + int resultClass = 0; + float prob = 1.0; + + resultClass = pCls->Evaluate(imgCls); //类别id + prob = pCls->getProbability(); //可能性 + + //切图保存,按需 + { + isDirExist(classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass)); + //存图数据入队,按需 + addImgToSaveStruct(imgCls, mysaveKey1, classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass) + "/" + mySN + myDataTime + QString::number(j) + ".bmp"); + } + + //此处应添加缺陷计数逻辑 + + if (resultClass == 0)//OK + { + ; + } + else if (resultClass == 1)//划伤Scratch + { + resIfNg = true; + //rectangle(sourceRectangles, rect1.tl(), rect1.br(), Scalar(0, 0, 255), 3, 8); + ////可行,必须转一下格式 + //{ + // QString pathQstr = "划伤"; + // QByteArray path_arr = pathQstr.toLocal8Bit(); + // const char* path = path_arr.data(); + // putTextZH(sourceRectangles, path, cv::Point(rect1.tl().x, rect1.tl().y - 5), Scalar(255, 0, 0), 50, "微软雅黑"); + //} + + defectInfo mdefectInfo; + mdefectInfo.defectName = "划伤"; + mdefectInfo.defectRect = rect1; + mdefectInfo.defectArea = contourArea(contours[j]); + mdefectInfo.defectScalar = cv::Scalar(0, 0, 255); + defectInfoList.enqueue(mdefectInfo); + } + else if (resultClass == 2)//碰伤 + { + resIfNg = true; + defectInfo mdefectInfo; + mdefectInfo.defectName = "碰伤"; + mdefectInfo.defectRect = rect1; + mdefectInfo.defectArea = contourArea(contours[j]); + mdefectInfo.defectScalar = cv::Scalar(0, 255, 255); + defectInfoList.enqueue(mdefectInfo); + } + imgCls.release(); + } + else { + //cout << "ng is too small<" << filterNum << endl; + continue; + } + } + } + //cv::Mat resMat = img2D.clone(); + sourceRectangles.release(); + contours.resize(0); + resultDstImg.release(); + } + */ + + detect_mutex->lock(); + //resMat = sourceImg.clone(); + //sourceImg.copyTo(resMat); + resMat.push_back(sourceImg); + //结果图画框和缺陷,在resMat上绘制 + if (resMat.size() > 0) + drawDefects(resMat[0], defectInfoList); + + //QByteArray,根据实际项目进行解析 + //参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double) + + 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 ) + addImgToSaveStruct(resMat[0], mysaveKey1, resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png"); + 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_2D3Ddata_module::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).toStdString(); + mySave.savePath = (savePath).toStdString(); + saveStruct.enqueue(mySave); + saveStruct_mutex->unlock(); +} +void Hd_AI_2D3Ddata_module::saveImgThread() +{ + while (1) + { + if (!ifdetectThread)return; + _sleep(1); + qApp->processEvents(); + if (saveStruct.size() < 1)continue; + for (int i = 0; i < saveStruct.size(); i++) + { + saveStruct_mutex->lock(); + saveImgStruct mysaveStruct = saveStruct.dequeue(); + saveStruct_mutex->unlock(); + //if (mysaveStruct.out_Mat.data)imwrite(mysaveStruct.savePath, mysaveStruct.out_Mat); + //std::cout << "savePath::" << mysaveStruct.savePath << std::endl; + _sleep(1); + } + } +} +//检测线程,应当只运行一次 +void Hd_AI_2D3Ddata_module::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); + +} + + +bool myMath(double a, double b) +{ + return a > b; +} + +static int runNum = 0; +QQueue timeAll_A; +QQueue timeAll_B; +bool Hd_AI_2D3Ddata_module::run() +{ + defectNums = 0; + vector srcMats; + + clock_t start = 0; + clock_t stop = 0; + double time = 0; + + //cv::Mat mat; + float prob = 0; + + if (HikMat.isEmpty()) + return true; + srcMats.push_back(HikMat[0]); + + start = clock(); + + + + stop = clock(); + time = stop - start; + timeAll_B.enqueue(time); + start = clock(); + + qSort(timeAll_A.begin(), timeAll_A.end(), myMath); + qSort(timeAll_B.begin(), timeAll_B.end(), myMath); + + timeAll_A.rend(); + timeAll_B.rend(); + + //Sleep(50); + //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 "imgproc/imgproc_c.h" + +#include "base.h" + + +#pragma execution_character_set("utf-8") + + + +//保存图片的结构体 +struct saveImgStruct; +//{ +// cv::Mat out_Mat; +// std::string saveKey; //关键字,用于LOG日志 +// std::string savePath; //保存路径 +//}; + +//初始化流程(一次):Hd_AI_2D3Ddata_module,initParas +//运行流程:writeData主线程把数据传过来,run,readData此线程把数据传主线程 +class Hd_AI_2D3Ddata_module :public Base +{ + Q_OBJECT +public: + Hd_AI_2D3Ddata_module(); + virtual ~Hd_AI_2D3Ddata_module(); + //从类中读数据到实例对象 + 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; + 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_2D3Ddata_module * create(); + Q_DECL_EXPORT void destory(Hd_AI_2D3Ddata_module * 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_2D3Ddata_module/base.h b/BaseModules/Hd_AI_2D3Ddata_module/base.h new file mode 100644 index 0000000..84d2f44 --- /dev/null +++ b/BaseModules/Hd_AI_2D3Ddata_module/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_empty_module/CMakeLists.txt b/BaseModules/Hd_AI_empty_module/CMakeLists.txt new file mode 100644 index 0000000..06f29aa --- /dev/null +++ b/BaseModules/Hd_AI_empty_module/CMakeLists.txt @@ -0,0 +1,70 @@ +# 获取获取当前目录名 +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") +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) + +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 + ) + + +# 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_empty_module/Hd_AI_empty_module.cpp b/BaseModules/Hd_AI_empty_module/Hd_AI_empty_module.cpp new file mode 100644 index 0000000..3827322 --- /dev/null +++ b/BaseModules/Hd_AI_empty_module/Hd_AI_empty_module.cpp @@ -0,0 +1,985 @@ +#include "Hd_AI_empty_module.h" +#include + +#include +#include + +#include +using namespace cv; + +//原图保存文件夹 + QString oriImgSaveDir = "F:/ORI/"; +//原图class图保存文件夹 + QString classImgSaveDir = "F:/ORIclass/"; +//结果保存文件夹 + QString resImgSaveDir = "F:/res/"; + +//切图的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 = ""; + + 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; + } + + +// 缺陷信息结构体 +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; + + + +//判断文件是否存在,不存在则创建 +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_empty_module::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_empty_module* create() +{ + qDebug() << "[INFO] " << " Hd_AI_empty_module create success!" ; + return new Hd_AI_empty_module; +} + +void destory(Hd_AI_empty_module* ptr) +{ + qDebug() << "[INFO] " << "Hd_AI_empty_module destory success!"; + if(ptr) + { + delete ptr; + ptr = nullptr; + } +} + +Hd_AI_empty_module::Hd_AI_empty_module() +{ + 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_empty_module::~Hd_AI_empty_module() +{ + 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_empty_module::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 + { + vec_mat.push_back(HikMat[0]); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << " resMat.data err:"; + } + if (allNum++ % 21 == 20) + data = "2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2";// resByte.dequeue(); + else + data = "1";//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_empty_module::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_empty_module::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(); + HikMat.enqueue(vec_mat[0]); + + QString savePath; + QString sData = data; + QStringList splitDataList = sData.split(","); + if(splitDataList.size()>=4) + if(splitDataList[2].contains('_')) + cameraKey_index = splitDataList[2]; + 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"; + } + + if (!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); + addImgToSaveStruct(HikMat[0], "0", savePath);// + } + + //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_empty_module::initParas(QByteArray& byteArray) +{ + QByteArrayList valueList = byteArray.split('+'); + QStringList valueiList; + + if (byteArray.contains("onlySetParameter")) + { + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,valueList[0]:" << valueList[0]; + if (valueList.size() > 1) + for (int i = 1; i < valueList.size(); i++) + { + QString valuei = byteArrayToUnicode(valueList[i]); + qDebug() << __FUNCTION__ << " line:" << __LINE__ << "onlySetParameter,lastName:" << valueList[0] << " ,valuei:" << valuei; + } + return true; + } + + + if (valueList.size() > 1) + for (int i = 1; i < valueList.size(); i++) + { + QString valuei = byteArrayToUnicode(valueList[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_empty_module::detectThread); + //运行存图线程 + QFuture future2 = QtConcurrent::run(this, &Hd_AI_empty_module::saveImgThread); + //detectThread(); + + //原图总文件夹 + isDirExist(oriImgSaveDir); + //原图class总文件夹 + isDirExist(classImgSaveDir); + //结果图总文件夹 + isDirExist(resImgSaveDir); + + + //qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ; + return true; +} +// 解码 +bool Hd_AI_empty_module::decodeAiValue(QByteArray byteArray,QVector &vec_ret) +{ + if(byteArray.isEmpty()) + { + qCritical() << "[Error] " << " byteArray is null!"; + return false; + } + + /*******************************************/ + byteArray.remove(0,8); + QByteArray mm_data = byteArray.mid(0,4); + memcpy((void*)¶mNum,(void*)mm_data.data(), 4); + qDebug() << "[INFO] " << " paramNum:" << paramNum; + byteArray.remove(0,4); + /*******************************************/ + + while (!byteArray.isEmpty()) + { + int type = 0; + QByteArray m_type = byteArray.mid(0,4); + memcpy((void*)&type,(void*)m_type.data(), 4); + byteArray.remove(0,4); + + int len = 0; + QByteArray m_len = byteArray.mid(0,4); + memcpy((void*)&len,(void*)m_len.data(), 4); + byteArray.remove(0,4); + + QByteArray mm_data = byteArray.mid(0,len); + vec_ret.append(mm_data); + byteArray.remove(0,len); + } + return true; +} + +//---------检测流程,根据实际项目修改--------- +bool Hd_AI_empty_module::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(); + //} + + QQueue defectInfoList; + + //seg后class检测逻辑 + /*if (0) { + Mat sourceRectangles;// 在原图缺陷位置,边界矩形框出 + sourceRectangles = sourceImg.clone(); + //sourceImg.copyTo(sourceRectangles); + vector> contours; + + //seg检测 + Mat resultDstImg;// = sourceImg.clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + resultDstImg = pSeg->Evaluate(sourceImg); + //int resultClass = pCls->Evaluate(sourceImg); //类别id + + //opencv对二值图进行处理 + //GaussianBlur(resultDstImg, resultDstImg, Size(3, 3), 1); //缺陷过滤值较大时,可以减少检出数;缺陷较小时,漏检较多 + threshold(resultDstImg, resultDstImg, pSeg->m_classes, 255, THRESH_BINARY_INV);//大于部分设为0,小于部分设为255 + //findContours(resultDstImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); //不识别内部轮廓,有些情况抓到的图像外框? + findContours(resultDstImg, contours, cv::noArray(), cv::RETR_TREE, cv::CHAIN_APPROX_NONE); //需要内部轮廓的写法 + + if (contours.size() == 0) + { + //printf("Notlabeled\n"); + } + else + { + for (int j = 0; j < contours.size(); j++) + { + Rect rect1 = boundingRect(Mat(contours[j])); + + //最小缺陷过滤参数,根据项目实际设置! + int filterNum = 10; + float w = rect1.width * 1.0; + float h = rect1.height * 1.0; + if ((w / sourceImg.cols > 0.98) || (h / sourceImg.rows > 0.98)) + { + //抓到图像外框了 + //qDebug() << "NG is too big..."; + continue; + } + //且逻辑,有的项目需要或逻辑!! + if ((w > filterNum && h > filterNum))//||rect1.area> filterNum*filterNum*1.5) + { + //class检测mat + Mat imgCls; + + //此段为切图逻辑为: + //缺陷外扩5像素; + //判断最大边,大于class建网大小的,按照最大边尺寸比例切大图,然后resize为建网大小;最大边小于等于建网大小的,按照建网大小切图; + //越界填充 + { + int startX = rect1.x - 5; + int startY = rect1.y - 5; + int endX = rect1.x + rect1.width + 5; + int endY = rect1.y + rect1.height + 5; + + //尺寸比例 + double maxscaler, scalerX, scalerY; + maxscaler = 1.0; + scalerX = (endX - startX) * 1.0 / sizeW;//*1.0转为float,避免被取整了 + scalerY = (endY - startY) * 1.0 / sizeH; + + maxscaler = max(maxscaler, max(scalerX, scalerY)); + + int avgX = (rect1.x + rect1.x + rect1.width) / 2; //中点X位置 + int avgY = (rect1.y + rect1.y + rect1.height) / 2;//中点Y位置 + + startX = avgX - int(maxscaler * sizeW / 2);//强转为int + endX = startX + int(maxscaler * sizeW); + startY = avgY - int(maxscaler * sizeH / 2); + endY = startY + int(maxscaler * sizeH); + + //判断是否越界,越界要填充的像素个数 + int copyTop = max(0, 0 - startY); + int copyButton = max(0, endY - sourceImg.rows); + int copyLeft = max(0, 0 - startX); + int copyRight = max(0, endX - sourceImg.cols); + + //切图不要越界,会崩溃 + Mat imgCut = sourceImg(Range(max(0, startY - copyTop), min(sourceImg.rows, endY - copyButton)), + Range(max(0, startX - copyLeft), min(sourceImg.cols, endX - copyRight))); //起始行,终止行;起始列,终止列 + + //图像扩充 + if (sourceImg.channels() == 1) + { + copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, fillColour); + } + else + { + copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, Scalar(fillColour, fillColour, fillColour)); + } + //resize为建网大小 + cv::resize(imgCut, imgCls, cv::Size(sizeW, sizeH)); + imgCut.release(); + } + + + int resultClass = 0; + float prob = 1.0; + + resultClass = pCls->Evaluate(imgCls); //类别id + prob = pCls->getProbability(); //可能性 + + //切图保存,按需 + { + isDirExist(classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass)); + //存图数据入队,按需 + addImgToSaveStruct(imgCls, mysaveKey1, classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass) + "/" + mySN + myDataTime + QString::number(j) + ".bmp"); + } + + //此处应添加缺陷计数逻辑 + + if (resultClass == 0)//OK + { + ; + } + else if (resultClass == 1)//划伤Scratch + { + resIfNg = true; + //rectangle(sourceRectangles, rect1.tl(), rect1.br(), Scalar(0, 0, 255), 3, 8); + ////可行,必须转一下格式 + //{ + // QString pathQstr = "划伤"; + // QByteArray path_arr = pathQstr.toLocal8Bit(); + // const char* path = path_arr.data(); + // putTextZH(sourceRectangles, path, cv::Point(rect1.tl().x, rect1.tl().y - 5), Scalar(255, 0, 0), 50, "微软雅黑"); + //} + + defectInfo mdefectInfo; + mdefectInfo.defectName = "划伤"; + mdefectInfo.defectRect = rect1; + mdefectInfo.defectArea = contourArea(contours[j]); + mdefectInfo.defectScalar = cv::Scalar(0, 0, 255); + defectInfoList.enqueue(mdefectInfo); + } + else if (resultClass == 2)//碰伤 + { + resIfNg = true; + defectInfo mdefectInfo; + mdefectInfo.defectName = "碰伤"; + mdefectInfo.defectRect = rect1; + mdefectInfo.defectArea = contourArea(contours[j]); + mdefectInfo.defectScalar = cv::Scalar(0, 255, 255); + defectInfoList.enqueue(mdefectInfo); + } + imgCls.release(); + } + else { + //cout << "ng is too small<" << filterNum << endl; + continue; + } + } + } + //cv::Mat resMat = img2D.clone(); + sourceRectangles.release(); + contours.resize(0); + resultDstImg.release(); + } + */ + + detect_mutex->lock(); + //resMat = sourceImg.clone(); + //sourceImg.copyTo(resMat); + resMat.push_back(sourceImg); + //结果图画框和缺陷,在resMat上绘制 + if (resMat.size() > 0) + drawDefects(resMat[0], defectInfoList); + + //QByteArray,根据实际项目进行解析 + //参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double) + + 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 ) + addImgToSaveStruct(resMat[0], mysaveKey1, resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png"); + 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_empty_module::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).toStdString(); + mySave.savePath = (savePath).toStdString(); + saveStruct.enqueue(mySave); + saveStruct_mutex->unlock(); +} +void Hd_AI_empty_module::saveImgThread() +{ + while (1) + { + if (!ifdetectThread)return; + _sleep(1); + 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_empty_module::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_empty_module::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" + +#pragma execution_character_set("utf-8") + + + +//保存图片的结构体 +struct saveImgStruct; +//{ +// cv::Mat out_Mat; +// std::string saveKey; //关键字,用于LOG日志 +// std::string savePath; //保存路径 +//}; + +//初始化流程(一次):Hd_AI_empty_module,initParas +//运行流程:writeData主线程把数据传过来,run,readData此线程把数据传主线程 +class Hd_AI_empty_module :public Base +{ + Q_OBJECT +public: + Hd_AI_empty_module(); + virtual ~Hd_AI_empty_module(); + //从类中读数据到实例对象 + 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_empty_module * create(); + Q_DECL_EXPORT void destory(Hd_AI_empty_module * 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_empty_module/base.h b/BaseModules/Hd_AI_empty_module/base.h new file mode 100644 index 0000000..84d2f44 --- /dev/null +++ b/BaseModules/Hd_AI_empty_module/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_seg_module/CMakeLists.txt b/BaseModules/Hd_AI_seg_module/CMakeLists.txt new file mode 100644 index 0000000..9dafdb5 --- /dev/null +++ b/BaseModules/Hd_AI_seg_module/CMakeLists.txt @@ -0,0 +1,70 @@ +# 获取获取当前目录名 +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" "handdleAI3.0") +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) + +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 + ) + + +# 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_seg_module/Hd_AI_seg_module.cpp b/BaseModules/Hd_AI_seg_module/Hd_AI_seg_module.cpp new file mode 100644 index 0000000..9c104e0 --- /dev/null +++ b/BaseModules/Hd_AI_seg_module/Hd_AI_seg_module.cpp @@ -0,0 +1,918 @@ +#include "Hd_AI_seg_module.h" +#include + +#include +#include + + +#pragma execution_character_set("utf-8") +using namespace cv; +using namespace std; + +//原图保存文件夹 +static QString oriImgSaveDir = "F:/ORI/"; +//原图class图保存文件夹 +static QString classImgSaveDir = "F:/ORIclass/"; +//结果保存文件夹 +static QString resImgSaveDir = "F:/res/"; + +//切图的class的width,根据项目实际设置! +static int sizeW = 150; +//切图的class的height,根据项目实际设置! +static int sizeH = 150; +//切图的class的width,根据项目实际设置!0为黑,255为白; +static int fillColour = 0; + + +//检测线程锁 +static QMutex* detect_mutex; +//保存线程锁 +static QMutex* saveStruct_mutex; +//检测线程运行标志 +static bool ifdetectThread = false; + + +// 缺陷信息结构体 +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; + + + +//判断文件是否存在,不存在则创建 +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_seg_module::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_seg_module* create() +{ + qDebug() << "[INFO] " << " Hd_AI_seg_module create success!" ; + return new Hd_AI_seg_module; +} + +void destory(Hd_AI_seg_module* ptr) +{ + qDebug() << "[INFO] " << "Hd_AI_seg_module destory success!"; + if(ptr) + { + delete ptr; + ptr = nullptr; + } +} + +Hd_AI_seg_module::Hd_AI_seg_module() +{ + detect_mutex = new QMutex; + saveStruct_mutex = new QMutex; + ifdetectThread = false; +} + +Hd_AI_seg_module::~Hd_AI_seg_module() +{ + 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);*/ +} +static int defectNums = 0; +//static cv::Mat resMat; +//从类中读数据到实例对象 +bool Hd_AI_seg_module::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 + { + vec_mat.push_back(HikMat[0]); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << " resMat.data err:"; + } + //if (allNum++ % 21 == 20) + // data = "2";// resByte.dequeue(); + //else + if (defectNums == 0) + data = "1";//1OK,2NG + else + data = "2"; + resByte.clear(); + ////之后根据结果类修改 + //if(resIfNg == true) + // data = "NG"; + //else data = "OK"; + detect_mutex->unlock(); + return true; +} +//从类中读数据到实例对象 +bool Hd_AI_seg_module::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; +} +//接收数据 +bool Hd_AI_seg_module::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(); + HikMat.enqueue(vec_mat[0]); + //测试相机的, + //while (HikMat[0].cols > 2000)cv::resize(HikMat[0], HikMat[0], cv::Size(), 0.1, 0.1); + //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(); + + //cv::Mat resultDstImg = vec_mat[0].clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + //resultDstImg = pSeg->Evaluate(resultDstImg); + //resultDstImg.release(); + + return true; +} +//初始化 +bool Hd_AI_seg_module::initParas(QByteArray& byteArray) +{ + //if(byteArray.isEmpty()) + //{ + // qCritical() << "[Error] " << " byteArray is null!"; + // return false; + //} + //QVector vec_ret; + + //decodeAiValue(byteArray,vec_ret); + //if(vec_ret.isEmpty()) + //{ + // qCritical() << "[Error] " << " vec_ret is null!"; + // return false; + //} + + ///*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_seg_module::detectThread); + ////运行存图线程 + //QFuture future2 = QtConcurrent::run(this, &Hd_AI_seg_module::saveImgThread); + ////detectThread(); + + ////原图总文件夹 + //isDirExist(oriImgSaveDir); + ////原图class总文件夹 + //isDirExist(classImgSaveDir); + ////结果图总文件夹 + //isDirExist(resImgSaveDir); + + + wstring taskPath = L"task\\haiwai_demo_model1\\haiwai_demo_model1.task"; + wcout.imbue(locale("zh_CN")); + + //bool ret = instance(taskPath, 0); + //if (!ret) + //{ + // //销毁实例 + // destroyInstance(); + // cout << "run task false\n"; + // return false; + //} + //vector srcMats; + //vector results; + ////传入图片vector 和 接收结果的容器 + //ret = PBTASK_EXPORT::run(srcMats, results);//初始化一下AI模型 + + //qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ; + return true; +} +// 解码 +bool Hd_AI_seg_module::decodeAiValue(QByteArray byteArray,QVector &vec_ret) +{ + if(byteArray.isEmpty()) + { + qCritical() << "[Error] " << " byteArray is null!"; + return false; + } + + /*******************************************/ + byteArray.remove(0,8); + QByteArray mm_data = byteArray.mid(0,4); + memcpy((void*)¶mNum,(void*)mm_data.data(), 4); + qDebug() << "[INFO] " << " paramNum:" << paramNum; + byteArray.remove(0,4); + /*******************************************/ + + while (!byteArray.isEmpty()) + { + int type = 0; + QByteArray m_type = byteArray.mid(0,4); + memcpy((void*)&type,(void*)m_type.data(), 4); + byteArray.remove(0,4); + + int len = 0; + QByteArray m_len = byteArray.mid(0,4); + memcpy((void*)&len,(void*)m_len.data(), 4); + byteArray.remove(0,4); + + QByteArray mm_data = byteArray.mid(0,len); + vec_ret.append(mm_data); + byteArray.remove(0,len); + } + return true; +} + +//---------检测流程,根据实际项目修改--------- +bool Hd_AI_seg_module::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(); + //} + + QQueue defectInfoList; + + //seg后class检测逻辑 + /*if (0) { + Mat sourceRectangles;// 在原图缺陷位置,边界矩形框出 + sourceRectangles = sourceImg.clone(); + //sourceImg.copyTo(sourceRectangles); + vector> contours; + + //seg检测 + Mat resultDstImg;// = sourceImg.clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + resultDstImg = pSeg->Evaluate(sourceImg); + //int resultClass = pCls->Evaluate(sourceImg); //类别id + + //opencv对二值图进行处理 + //GaussianBlur(resultDstImg, resultDstImg, Size(3, 3), 1); //缺陷过滤值较大时,可以减少检出数;缺陷较小时,漏检较多 + threshold(resultDstImg, resultDstImg, pSeg->m_classes, 255, THRESH_BINARY_INV);//大于部分设为0,小于部分设为255 + //findContours(resultDstImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); //不识别内部轮廓,有些情况抓到的图像外框? + findContours(resultDstImg, contours, cv::noArray(), cv::RETR_TREE, cv::CHAIN_APPROX_NONE); //需要内部轮廓的写法 + + if (contours.size() == 0) + { + //printf("Notlabeled\n"); + } + else + { + for (int j = 0; j < contours.size(); j++) + { + Rect rect1 = boundingRect(Mat(contours[j])); + + //最小缺陷过滤参数,根据项目实际设置! + int filterNum = 10; + float w = rect1.width * 1.0; + float h = rect1.height * 1.0; + if ((w / sourceImg.cols > 0.98) || (h / sourceImg.rows > 0.98)) + { + //抓到图像外框了 + //qDebug() << "NG is too big..."; + continue; + } + //且逻辑,有的项目需要或逻辑!! + if ((w > filterNum && h > filterNum))//||rect1.area> filterNum*filterNum*1.5) + { + //class检测mat + Mat imgCls; + + //此段为切图逻辑为: + //缺陷外扩5像素; + //判断最大边,大于class建网大小的,按照最大边尺寸比例切大图,然后resize为建网大小;最大边小于等于建网大小的,按照建网大小切图; + //越界填充 + { + int startX = rect1.x - 5; + int startY = rect1.y - 5; + int endX = rect1.x + rect1.width + 5; + int endY = rect1.y + rect1.height + 5; + + //尺寸比例 + double maxscaler, scalerX, scalerY; + maxscaler = 1.0; + scalerX = (endX - startX) * 1.0 / sizeW;//*1.0转为float,避免被取整了 + scalerY = (endY - startY) * 1.0 / sizeH; + + maxscaler = max(maxscaler, max(scalerX, scalerY)); + + int avgX = (rect1.x + rect1.x + rect1.width) / 2; //中点X位置 + int avgY = (rect1.y + rect1.y + rect1.height) / 2;//中点Y位置 + + startX = avgX - int(maxscaler * sizeW / 2);//强转为int + endX = startX + int(maxscaler * sizeW); + startY = avgY - int(maxscaler * sizeH / 2); + endY = startY + int(maxscaler * sizeH); + + //判断是否越界,越界要填充的像素个数 + int copyTop = max(0, 0 - startY); + int copyButton = max(0, endY - sourceImg.rows); + int copyLeft = max(0, 0 - startX); + int copyRight = max(0, endX - sourceImg.cols); + + //切图不要越界,会崩溃 + Mat imgCut = sourceImg(Range(max(0, startY - copyTop), min(sourceImg.rows, endY - copyButton)), + Range(max(0, startX - copyLeft), min(sourceImg.cols, endX - copyRight))); //起始行,终止行;起始列,终止列 + + //图像扩充 + if (sourceImg.channels() == 1) + { + copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, fillColour); + } + else + { + copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, Scalar(fillColour, fillColour, fillColour)); + } + //resize为建网大小 + cv::resize(imgCut, imgCls, cv::Size(sizeW, sizeH)); + imgCut.release(); + } + + + int resultClass = 0; + float prob = 1.0; + + resultClass = pCls->Evaluate(imgCls); //类别id + prob = pCls->getProbability(); //可能性 + + //切图保存,按需 + { + isDirExist(classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass)); + //存图数据入队,按需 + addImgToSaveStruct(imgCls, mysaveKey1, classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass) + "/" + mySN + myDataTime + QString::number(j) + ".bmp"); + } + + //此处应添加缺陷计数逻辑 + + if (resultClass == 0)//OK + { + ; + } + else if (resultClass == 1)//划伤Scratch + { + resIfNg = true; + //rectangle(sourceRectangles, rect1.tl(), rect1.br(), Scalar(0, 0, 255), 3, 8); + ////可行,必须转一下格式 + //{ + // QString pathQstr = "划伤"; + // QByteArray path_arr = pathQstr.toLocal8Bit(); + // const char* path = path_arr.data(); + // putTextZH(sourceRectangles, path, cv::Point(rect1.tl().x, rect1.tl().y - 5), Scalar(255, 0, 0), 50, "微软雅黑"); + //} + + defectInfo mdefectInfo; + mdefectInfo.defectName = "划伤"; + mdefectInfo.defectRect = rect1; + mdefectInfo.defectArea = contourArea(contours[j]); + mdefectInfo.defectScalar = cv::Scalar(0, 0, 255); + defectInfoList.enqueue(mdefectInfo); + } + else if (resultClass == 2)//碰伤 + { + resIfNg = true; + defectInfo mdefectInfo; + mdefectInfo.defectName = "碰伤"; + mdefectInfo.defectRect = rect1; + mdefectInfo.defectArea = contourArea(contours[j]); + mdefectInfo.defectScalar = cv::Scalar(0, 255, 255); + defectInfoList.enqueue(mdefectInfo); + } + imgCls.release(); + } + else { + //cout << "ng is too small<" << filterNum << endl; + continue; + } + } + } + //cv::Mat resMat = img2D.clone(); + sourceRectangles.release(); + contours.resize(0); + resultDstImg.release(); + } + */ + + detect_mutex->lock(); + //resMat = sourceImg.clone(); + //sourceImg.copyTo(resMat); + resMat.push_back(sourceImg); + //结果图画框和缺陷,在resMat上绘制 + if (resMat.size() > 0) + drawDefects(resMat[0], defectInfoList); + + //QByteArray,根据实际项目进行解析 + //参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double) + + 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 ) + addImgToSaveStruct(resMat[0], mysaveKey1, resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png"); + 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_seg_module::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).toStdString(); + mySave.savePath = (savePath).toStdString(); + saveStruct.enqueue(mySave); + saveStruct_mutex->unlock(); +} +void Hd_AI_seg_module::saveImgThread() +{ + while (1) + { + if (!ifdetectThread)return; + _sleep(1); + qApp->processEvents(); + if (saveStruct.size() < 1)continue; + for (int i = 0; i < saveStruct.size(); i++) + { + saveStruct_mutex->lock(); + saveImgStruct mysaveStruct = saveStruct.dequeue(); + saveStruct_mutex->unlock(); + //if (mysaveStruct.out_Mat.data)imwrite(mysaveStruct.savePath, mysaveStruct.out_Mat); + //std::cout << "savePath::" << mysaveStruct.savePath << std::endl; + _sleep(1); + } + } +} +//检测线程,应当只运行一次 +void Hd_AI_seg_module::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); + +} + + +bool myMath(double a, double b) +{ + return a > b; +} + +static int runNum = 0; +QQueue timeAll_A; +QQueue timeAll_B; +bool Hd_AI_seg_module::run() +{ + defectNums = 0; + vector srcMats; + + clock_t start = 0; + clock_t stop = 0; + double time = 0; + + //cv::Mat mat; + float prob = 0; + + if (HikMat.isEmpty()) + return false; + srcMats.push_back(HikMat[0]); + + start = clock(); + + + + stop = clock(); + time = stop - start; + timeAll_B.enqueue(time); + start = clock(); + + qSort(timeAll_A.begin(), timeAll_A.end(), myMath); + qSort(timeAll_B.begin(), timeAll_B.end(), myMath); + + timeAll_A.rend(); + timeAll_B.rend(); + + //Sleep(50); + //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 "imgproc/imgproc_c.h" + +#include "base.h" + + +#pragma execution_character_set("utf-8") + + + +//保存图片的结构体 +struct saveImgStruct; +//{ +// cv::Mat out_Mat; +// std::string saveKey; //关键字,用于LOG日志 +// std::string savePath; //保存路径 +//}; + +//初始化流程(一次):Hd_AI_seg_module,initParas +//运行流程:writeData主线程把数据传过来,run,readData此线程把数据传主线程 +class Hd_AI_seg_module :public Base +{ + Q_OBJECT +public: + Hd_AI_seg_module(); + virtual ~Hd_AI_seg_module(); + //从类中读数据到实例对象 + 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 byteArray,QVector &vec_ret); + //---------检测流程,根据实际项目修改--------- + bool AIdetect(cv::Mat img2D, QString saveKey, QString Dir2D, QString SN, QString DataTime); + + + +private: + + int allNum = 0; + int StationNo; + 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_seg_module * create(); + Q_DECL_EXPORT void destory(Hd_AI_seg_module * 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_seg_module/base.h b/BaseModules/Hd_AI_seg_module/base.h new file mode 100644 index 0000000..84d2f44 --- /dev/null +++ b/BaseModules/Hd_AI_seg_module/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_tmp_module_savImg/CMakeLists.txt b/BaseModules/Hd_AI_tmp_module_savImg/CMakeLists.txt new file mode 100644 index 0000000..06f29aa --- /dev/null +++ b/BaseModules/Hd_AI_tmp_module_savImg/CMakeLists.txt @@ -0,0 +1,70 @@ +# 获取获取当前目录名 +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") +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) + +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 + ) + + +# 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_tmp_module_savImg/Hd_AI_tmp_module_savImg.cpp b/BaseModules/Hd_AI_tmp_module_savImg/Hd_AI_tmp_module_savImg.cpp new file mode 100644 index 0000000..b070c61 --- /dev/null +++ b/BaseModules/Hd_AI_tmp_module_savImg/Hd_AI_tmp_module_savImg.cpp @@ -0,0 +1,1010 @@ +#include "Hd_AI_tmp_module_savImg.h" +#include + +#include +#include + +#include +using namespace cv; + +//原图保存文件夹 需要手动设置 + QString oriImgSaveDir = "D:/601线/超声波终焊01号机/极性检测/"; +//原图class图保存文件夹 需要手动设置 + QString classImgSaveDir = "F:/ORIclass/"; +//结果保存文件夹 需要手动设置 + QString resImgSaveDir = "F:/res/"; + +//切图的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; + + 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; + } + + +// 缺陷信息结构体 +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; + + + +//判断文件是否存在,不存在则创建 +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_tmp_module_savImg::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_tmp_module_savImg* create() +{ + qDebug() << "[INFO] " << " Hd_AI_tmp_module_savImg create success!" ; + return new Hd_AI_tmp_module_savImg; +} + +void destory(Hd_AI_tmp_module_savImg* ptr) +{ + qDebug() << "[INFO] " << "Hd_AI_tmp_module_savImg destory success!"; + if(ptr) + { + delete ptr; + ptr = nullptr; + } +} + +Hd_AI_tmp_module_savImg::Hd_AI_tmp_module_savImg() +{ + 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_tmp_module_savImg::~Hd_AI_tmp_module_savImg() +{ + 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_tmp_module_savImg::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*0*0";//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_tmp_module_savImg::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_tmp_module_savImg::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"; + }*/ + + + + + 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_tmp_module_savImg::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_tmp_module_savImg::detectThread); + //运行存图线程 + QFuture future2 = QtConcurrent::run(this, &Hd_AI_tmp_module_savImg::saveImgThread); + //detectThread(); + + //原图总文件夹 + isDirExist(oriImgSaveDir); + //原图class总文件夹 + isDirExist(classImgSaveDir); + //结果图总文件夹 + isDirExist(resImgSaveDir); + + + //qDebug() << "[INFO] " << " StationNo:" << StationNo << " img2D:" << img2D ; + return true; +} +// 解码 +bool Hd_AI_tmp_module_savImg::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_tmp_module_savImg::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(); + //} + + QQueue defectInfoList; + + //seg后class检测逻辑 + /*if (0) { + Mat sourceRectangles;// 在原图缺陷位置,边界矩形框出 + sourceRectangles = sourceImg.clone(); + //sourceImg.copyTo(sourceRectangles); + vector> contours; + + //seg检测 + Mat resultDstImg;// = sourceImg.clone();// = pSeg->Evaluate(sourceImg); //返回单通道二值图 图片错误输出Image Error!但不报错。。 + resultDstImg = pSeg->Evaluate(sourceImg); + //int resultClass = pCls->Evaluate(sourceImg); //类别id + + //opencv对二值图进行处理 + //GaussianBlur(resultDstImg, resultDstImg, Size(3, 3), 1); //缺陷过滤值较大时,可以减少检出数;缺陷较小时,漏检较多 + threshold(resultDstImg, resultDstImg, pSeg->m_classes, 255, THRESH_BINARY_INV);//大于部分设为0,小于部分设为255 + //findContours(resultDstImg, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); //不识别内部轮廓,有些情况抓到的图像外框? + findContours(resultDstImg, contours, cv::noArray(), cv::RETR_TREE, cv::CHAIN_APPROX_NONE); //需要内部轮廓的写法 + + if (contours.size() == 0) + { + //printf("Notlabeled\n"); + } + else + { + for (int j = 0; j < contours.size(); j++) + { + Rect rect1 = boundingRect(Mat(contours[j])); + + //最小缺陷过滤参数,根据项目实际设置! + int filterNum = 10; + float w = rect1.width * 1.0; + float h = rect1.height * 1.0; + if ((w / sourceImg.cols > 0.98) || (h / sourceImg.rows > 0.98)) + { + //抓到图像外框了 + //qDebug() << "NG is too big..."; + continue; + } + //且逻辑,有的项目需要或逻辑!! + if ((w > filterNum && h > filterNum))//||rect1.area> filterNum*filterNum*1.5) + { + //class检测mat + Mat imgCls; + + //此段为切图逻辑为: + //缺陷外扩5像素; + //判断最大边,大于class建网大小的,按照最大边尺寸比例切大图,然后resize为建网大小;最大边小于等于建网大小的,按照建网大小切图; + //越界填充 + { + int startX = rect1.x - 5; + int startY = rect1.y - 5; + int endX = rect1.x + rect1.width + 5; + int endY = rect1.y + rect1.height + 5; + + //尺寸比例 + double maxscaler, scalerX, scalerY; + maxscaler = 1.0; + scalerX = (endX - startX) * 1.0 / sizeW;//*1.0转为float,避免被取整了 + scalerY = (endY - startY) * 1.0 / sizeH; + + maxscaler = max(maxscaler, max(scalerX, scalerY)); + + int avgX = (rect1.x + rect1.x + rect1.width) / 2; //中点X位置 + int avgY = (rect1.y + rect1.y + rect1.height) / 2;//中点Y位置 + + startX = avgX - int(maxscaler * sizeW / 2);//强转为int + endX = startX + int(maxscaler * sizeW); + startY = avgY - int(maxscaler * sizeH / 2); + endY = startY + int(maxscaler * sizeH); + + //判断是否越界,越界要填充的像素个数 + int copyTop = max(0, 0 - startY); + int copyButton = max(0, endY - sourceImg.rows); + int copyLeft = max(0, 0 - startX); + int copyRight = max(0, endX - sourceImg.cols); + + //切图不要越界,会崩溃 + Mat imgCut = sourceImg(Range(max(0, startY - copyTop), min(sourceImg.rows, endY - copyButton)), + Range(max(0, startX - copyLeft), min(sourceImg.cols, endX - copyRight))); //起始行,终止行;起始列,终止列 + + //图像扩充 + if (sourceImg.channels() == 1) + { + copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, fillColour); + } + else + { + copyMakeBorder(imgCut, imgCut, copyTop, copyButton, copyLeft, copyRight, BORDER_CONSTANT, Scalar(fillColour, fillColour, fillColour)); + } + //resize为建网大小 + cv::resize(imgCut, imgCls, cv::Size(sizeW, sizeH)); + imgCut.release(); + } + + + int resultClass = 0; + float prob = 1.0; + + resultClass = pCls->Evaluate(imgCls); //类别id + prob = pCls->getProbability(); //可能性 + + //切图保存,按需 + { + isDirExist(classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass)); + //存图数据入队,按需 + addImgToSaveStruct(imgCls, mysaveKey1, classImgSaveDir + mysaveKey1 + "/class_" + QString::number(resultClass) + "/" + mySN + myDataTime + QString::number(j) + ".bmp"); + } + + //此处应添加缺陷计数逻辑 + + if (resultClass == 0)//OK + { + ; + } + else if (resultClass == 1)//划伤Scratch + { + resIfNg = true; + //rectangle(sourceRectangles, rect1.tl(), rect1.br(), Scalar(0, 0, 255), 3, 8); + ////可行,必须转一下格式 + //{ + // QString pathQstr = "划伤"; + // QByteArray path_arr = pathQstr.toLocal8Bit(); + // const char* path = path_arr.data(); + // putTextZH(sourceRectangles, path, cv::Point(rect1.tl().x, rect1.tl().y - 5), Scalar(255, 0, 0), 50, "微软雅黑"); + //} + + defectInfo mdefectInfo; + mdefectInfo.defectName = "划伤"; + mdefectInfo.defectRect = rect1; + mdefectInfo.defectArea = contourArea(contours[j]); + mdefectInfo.defectScalar = cv::Scalar(0, 0, 255); + defectInfoList.enqueue(mdefectInfo); + } + else if (resultClass == 2)//碰伤 + { + resIfNg = true; + defectInfo mdefectInfo; + mdefectInfo.defectName = "碰伤"; + mdefectInfo.defectRect = rect1; + mdefectInfo.defectArea = contourArea(contours[j]); + mdefectInfo.defectScalar = cv::Scalar(0, 255, 255); + defectInfoList.enqueue(mdefectInfo); + } + imgCls.release(); + } + else { + //cout << "ng is too small<" << filterNum << endl; + continue; + } + } + } + //cv::Mat resMat = img2D.clone(); + sourceRectangles.release(); + contours.resize(0); + resultDstImg.release(); + } + */ + + detect_mutex->lock(); + //resMat = sourceImg.clone(); + //sourceImg.copyTo(resMat); + resMat.push_back(sourceImg); + //结果图画框和缺陷,在resMat上绘制 + if (resMat.size() > 0) + drawDefects(resMat[0], defectInfoList); + + //QByteArray,根据实际项目进行解析 + //参数,例:返回结果图;相机关键字(SN或userID)+保存路径+缺陷信息列表(缺陷名称,缺陷位置int(x,y,w,h),画框颜色Scalar(a,b,c),缺陷面积double) + + 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 ) + addImgToSaveStruct(resMat[0], mysaveKey1, resImgSaveDir + mysaveKey1 + "/" + mySN + myDataTime + ".png"); + 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_tmp_module_savImg::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_tmp_module_savImg::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_tmp_module_savImg::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_tmp_module_savImg::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" + +#pragma execution_character_set("utf-8") + + + +//保存图片的结构体 +struct saveImgStruct; +//{ +// cv::Mat out_Mat; +// std::string saveKey; //关键字,用于LOG日志 +// std::string savePath; //保存路径 +//}; + +//初始化流程(一次):Hd_AI_tmp_module_savImg,initParas +//运行流程:writeData主线程把数据传过来,run,readData此线程把数据传主线程 +class Hd_AI_tmp_module_savImg :public Base +{ + Q_OBJECT +public: + Hd_AI_tmp_module_savImg(); + virtual ~Hd_AI_tmp_module_savImg(); + //从类中读数据到实例对象 + 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_tmp_module_savImg * create(); + Q_DECL_EXPORT void destory(Hd_AI_tmp_module_savImg * 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_tmp_module_savImg/base.h b/BaseModules/Hd_AI_tmp_module_savImg/base.h new file mode 100644 index 0000000..00bd6b2 --- /dev/null +++ b/BaseModules/Hd_AI_tmp_module_savImg/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_camera_thread.cpp b/BaseModules/Hd_LogicDll_module/Logic_camera_thread.cpp index f96fd8d..7ec0544 100644 --- a/BaseModules/Hd_LogicDll_module/Logic_camera_thread.cpp +++ b/BaseModules/Hd_LogicDll_module/Logic_camera_thread.cpp @@ -21,6 +21,7 @@ bool Logic_camera_thread::setModule( QString station_name,MyModule module,QJsonO m_name = obj.value("相机key").toString(); m_type = "camera";// camera_obj.value("camera_type").toString(); getImgTimeout = obj.value("取图超时").toString().toInt(); + maxCheckTime = obj.value("拍照信号-检查完成的时间").toString().toDouble(); if (m_name.isEmpty() || m_type.isEmpty()) { qCritical() << "[Error] " << " camera_key or camera_type is null"; @@ -76,6 +77,7 @@ void Logic_camera_thread::set_m_stoprun(bool val) QMutexLocker locker(ifMissImage_mutex); // 加锁 m_stoprun = val; } +clock_t maxTime_start, maxTime_stop; void Logic_camera_thread::run() { while (ifRun) @@ -86,7 +88,7 @@ void Logic_camera_thread::run() m_stoprun = false; getImg_faileNums = 0; ifsaveImage = false; - //set_ifMissImage(false); + set_ifMissImage(false); cu_runNum = 0; int last_addImageNums = addImageNums; maxTime_start = clock(); @@ -288,14 +290,9 @@ bool Logic_camera_thread::runCameraModule() //QMutexLocker locker(ifMissImage_mutex); // 加锁 QByteArray task_name; std::vector camera_mat; - if (CommunicationProcessLevel == 2 && waitenum++%100==0) - { - //missImgae_addImage(camera_mat); - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " m_name:" << m_name << " waite trigger!"<< waitenum; - } if (CommunicationProcessLevel == 2 && maxTime_stop - maxTime_start > maxCheckTime) { - qCritical() << __FUNCTION__ << " line:" << __LINE__ << "maxCheckTime! add img, hardware" << m_Module.module_key; + qCritical() << __FUNCTION__ << " line:" << __LINE__ << " m_name:" << m_name << " maxTime_stop - maxTime_start > maxCheckTime,missImgae_addImage!"; missImgae_addImage(camera_mat); } else @@ -306,16 +303,8 @@ bool Logic_camera_thread::runCameraModule() { Sleep(10); qApp->processEvents(); - myTime += 10; - if (myTime % 1000 == 0) - { - if (CommunicationProcessLevel == 2) - { - //missImgae_addImage(camera_mat); - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " m_name:" << m_name << " waite trigger!"; - } - } - if (myTime > maxCheckTime) + maxTime_stop = clock(); + if (maxTime_stop - maxTime_start > getImgTimeout) { if (CommunicationProcessLevel == 2) { @@ -488,12 +477,12 @@ void Logic_camera_thread::setMutex(QMutex *mutex) m_mutex = new QMutex;// mutex; } -bool Logic_camera_thread::sendDataCommond( QString m_type,cv::Mat mat,QByteArray byteArray ) +bool Logic_camera_thread::sendDataCommond( QString m_type,cv::Mat mat,QByteArray byteArray) { QMutexLocker locker(m_mutex); dataPack m_pack; m_pack.mat = mat; - m_pack.byteArray = byteArray; + m_pack.byteArray = byteArray; m_pack.retFlag = m_type; m_pack.cameraName = m_name; diff --git a/BaseModules/Hd_LogicDll_module/Logic_camera_thread.h b/BaseModules/Hd_LogicDll_module/Logic_camera_thread.h index 24d5c51..2f3e911 100644 --- a/BaseModules/Hd_LogicDll_module/Logic_camera_thread.h +++ b/BaseModules/Hd_LogicDll_module/Logic_camera_thread.h @@ -1,4 +1,4 @@ -#ifndef Logic_camera_thread_H +#ifndef Logic_camera_thread_H #define Logic_camera_thread_H #include @@ -17,6 +17,7 @@ #include #include #include +#include #include "struct.h" #include "Logic_hdataintegration.h" @@ -32,7 +33,7 @@ public: void setRunFlag(bool flag){ m_stoprun = flag; } void setMutex( QMutex *mutex); void setSaveImage( QString save){ m_saveImage = save;} - bool sendDataCommond(QString m_type,cv::Mat mat,QByteArray byteArray); + bool sendDataCommond(QString m_type,cv::Mat mat,QByteArray byte); bool runCameraModule(); bool saveImage(std::vector mat); //设置相机key、取图超时、模块、拍照面描述、触发方式 @@ -82,8 +83,8 @@ public: QMutex* m_iomutex = nullptr; Base* finstcp_ptr = nullptr; QByteArray send_byte; - int maxCheckTime = 3000; - int waitenum = 0; + double maxCheckTime = 3000; + private: QQueue mydataIntegrationThread ; QObject *m_obj; @@ -106,8 +107,6 @@ private: QMutex* ifMissImage_mutex = nullptr; //相机数据包 QQueue camera_ImageDataPack; - int hardNum = 0; - clock_t maxTime_start, maxTime_stop; signals: }; diff --git a/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.cpp b/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.cpp index 368b59e..24a420c 100644 --- a/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.cpp +++ b/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.cpp @@ -485,7 +485,7 @@ QByteArray HAlgorithmData::getAlgorithmData(QString SNdataTime,QString cameraNam QByteArray HAlgorithmData::getIndexData(Algorithm_Result_Data result_data, int result_index, QString algorithm_result_index) { - //qDebug() << __FUNCTION__ << " line: " << __LINE__ << " algorithmKey:" << algorithmKey << " cameraName_and_index: " << cameraName_and_index << " result_data.byteArray len:" << result_data.byteArray.length(); + //qDebug() << __FUNCTION__ << " line: " << __LINE__ << " algorithmKey:" << algorithmKey << " cameraName_and_index: " << cameraName_and_index << " result_data.byte len:" << result_data.byte.length(); Algorithm_Result_Data_index index_pack; if (map_delete_Result_index.keys().contains(result_data.key_index)) @@ -503,10 +503,10 @@ QByteArray HAlgorithmData::getIndexData(Algorithm_Result_Data result_data, int r QString cameraName = list.at(0); QString task_index = list.at(1); //Logic_taskthread运行算法/汇总模块后,返回的数据 - QByteArray byteArray = result_data.byteArray; + QByteArray byte = result_data.byteArray; //result_data.byte转 QByteArrayList vec_Data; - vec_Data = byteArray.split('#'); + vec_Data = byte.split('#'); if (vec_Data.isEmpty()) { @@ -520,7 +520,7 @@ QByteArray HAlgorithmData::getIndexData(Algorithm_Result_Data result_data, int r return index_byte; } - index_byte = vec_Data[algorithm_result_index.toInt()];// byteArray;// QByteArray::fromStdString(sendMsg); + index_byte = vec_Data[algorithm_result_index.toInt()];// byte;// QByteArray::fromStdString(sendMsg); // 删除map中的数据 if (vec_Data.size() == 1) diff --git a/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.h b/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.h index dfe6033..a80fe8b 100644 --- a/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.h +++ b/BaseModules/Hd_LogicDll_module/Logic_hdataprocess.h @@ -22,7 +22,7 @@ typedef struct //or检测:保存路径,拍照面描述,相机key_第几张图,SN_时间戳 //and检测:保存路径,拍照面描述,相机key_第几张图,相机key_第几张图,SN_时间戳,SN_时间戳 //汇总:保存路径,触发信号_序号列表0,,SN_时间戳 - QByteArray byteArray; + QByteArray byteArray; std::vector dstImageMat; //拍照面描述 QString Surface; @@ -104,8 +104,8 @@ public: int m_index = 0; int image_queue_num = 0; //划分相机任务的数量 - QString cameraKey; private: + QString cameraKey; QJsonArray readSnFormatArray; // 存储sn码的格式 QVector*>> vector_vector_queue_mat_ptr; // 存储相机图片任务的容器;QVector*>为通道的 QMutex* m_mutex; diff --git a/BaseModules/Hd_LogicDll_module/Logic_myiothread.cpp b/BaseModules/Hd_LogicDll_module/Logic_myiothread.cpp index dfe1a49..033a399 100644 --- a/BaseModules/Hd_LogicDll_module/Logic_myiothread.cpp +++ b/BaseModules/Hd_LogicDll_module/Logic_myiothread.cpp @@ -187,6 +187,7 @@ void Logic_myiothread::run() // 一个信号取多张的情况,取图时间长,和通信线程分开。 m_runAndCheck_cameraThread->start(); int times = 0; + heart_start = clock(); while ( io_runflag ) { //心跳(定时向PLC的指定寄存器写入值)代码。 @@ -194,92 +195,65 @@ void Logic_myiothread::run() { //break; //times++; - Sleep(10); - qApp->processEvents(); - if ((m_heartTime / 10) <= times++) + + QStringList m_heartCmdList = m_heartCmd.split("#"); + for (int m = 0; m < m_heartCmdList.size(); m++) { - if (!m_heartCmd.isEmpty()) - {// 发送心跳 - ////QMutexLocker locker(m_iomutex);; - QByteArray byteArray = m_heartCmd.toUtf8(); // 设置 从起始位开始读取的指令 - if (!finstcp_ptr->writeData(vec_mat, byteArray)) - { - qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr reciveData error!"; - } - times = 0; - } - } - else - { - if (!rest_signal.isEmpty()) + Sleep(10); + qApp->processEvents(); + heart_stop = clock(); + //if ((m_heartTime / 10) <= times++) + if (heart_stop - heart_start >= m_heartTime) { - if (times % int(m_heartTime / 50 + 1) == 0) - { - // 修改读取多个命令 - //QMutexLocker locker(m_iomutex);; - QByteArray byteArray = readAll_cmd.toUtf8(); // 设置 从起始位开始读取的指令 - if (!finstcp_ptr->writeData(vec_mat, byteArray)) + heart_start = clock(); + if (!m_heartCmdList[m].isEmpty()) + {// 发送心跳 + ////QMutexLocker locker(m_iomutex);; + QByteArray byte = m_heartCmdList[m].toUtf8(); // 设置 从起始位开始读取的指令 + if (!finstcp_ptr->writeData(vec_mat, byte)) { qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr reciveData error!"; } - - // 读取到的信息解析 - QByteArray readByte; - if (!finstcp_ptr->readData(vec_mat, readByte)) + times = 0; + } + } + else + { + if (!rest_signal.isEmpty()) + { + if (times % int(m_heartTime / 50 + 1) == 0) { - qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr sendData error!"; - } - - if (readByte.isEmpty()) - return; - QByteArray readsnByte = readByte; - QVector vec_value; - while (!readByte.isEmpty()) - { - QByteArray retByte = readByte.mid(0, 2); - readByte.remove(0, 2); - - QString msg = retByte.toHex(); - int number_tmp = msg.toInt(NULL, 16); - vec_value.push_back(number_tmp); - } - - QString m_startpos = rest_signal.split("_")[0]; - QString m_value = rest_signal.split("_")[1]; - int trigger_index = m_startpos.mid(1, m_startpos.length() - 1).toInt(); - trigger_index -= trigger_startpos; - if (trigger_index < 0) - { - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " vec_value is error! read_sn_startpos:" << m_startpos; - } - else if (vec_value.size() > trigger_index) - { - //读复位标志满足 - if (vec_value.at(trigger_index) == m_value.toInt()) + // 修改读取多个命令 + //QMutexLocker locker(m_iomutex);; + QByteArray byte = readAll_cmd.toUtf8(); // 设置 从起始位开始读取的指令 + if (!finstcp_ptr->writeData(vec_mat, byte)) { - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " 复位信号:" << rest_signal; - foreach(QString m_station_name, station_list.keys()) - for (int i = 0; i < station_list.value(m_station_name)->sndata_list.size(); i++) - { - QStringList cameraList; - cameraList.append("all"); - for (int i = 0; i < 10; i++) - station_list.value(m_station_name)->clearData("all", cameraList); - } - //完成清标志 - QByteArray send_byte = rest_signal_back.toUtf8(); - if (!finstcp_ptr->writeData(vec_mat, send_byte)) - { - qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr reciveData error! "; - return; - } + qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr reciveData error!"; } - } - //程序号;传到到主界面类,再进行切换 - if (!program_Num.isEmpty()) - { - QString m_startpos = program_Num;// .split("_")[0]; - //QString m_value = program_Num.split("_")[1]; + + // 读取到的信息解析 + QByteArray readByte; + if (!finstcp_ptr->readData(vec_mat, readByte)) + { + qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr sendData error!"; + } + + if (readByte.isEmpty()) + return; + QByteArray readsnByte = readByte; + QVector vec_value; + while (!readByte.isEmpty()) + { + QByteArray retByte = readByte.mid(0, 2); + readByte.remove(0, 2); + + QString msg = retByte.toHex(); + int number_tmp = msg.toInt(NULL, 16); + vec_value.push_back(number_tmp); + } + + QString m_startpos = rest_signal.split("_")[0]; + QString m_value = rest_signal.split("_")[1]; int trigger_index = m_startpos.mid(1, m_startpos.length() - 1).toInt(); trigger_index -= trigger_startpos; if (trigger_index < 0) @@ -289,20 +263,19 @@ void Logic_myiothread::run() else if (vec_value.size() > trigger_index) { //读复位标志满足 - if (vec_value.at(trigger_index) !=0) + if (vec_value.at(trigger_index) == m_value.toInt()) { - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " 程序号:" << vec_value.at(trigger_index); - - dataPack m_data; - m_data.algorithmName = "程序号"; - m_data.cameraName = m_startpos; - m_data.byteArray = QByteArray::number(vec_value.at(trigger_index)); - m_data.retFlag = "algorithm"; - - QMutexLocker locker(sendProcess_mutex); - sendProcessTask.enqueue(m_data); + qCritical() << __FUNCTION__ << " line:" << __LINE__ << " 复位信号:" << rest_signal; + foreach(QString m_station_name, station_list.keys()) + for (int i = 0; i < station_list.value(m_station_name)->sndata_list.size(); i++) + { + QStringList cameraList; + cameraList.append("all"); + for (int i = 0; i < 10; i++) + station_list.value(m_station_name)->clearData("all", cameraList); + } //完成清标志 - QByteArray send_byte = program_Num_back.toUtf8(); + QByteArray send_byte = rest_signal_back.toUtf8(); if (!finstcp_ptr->writeData(vec_mat, send_byte)) { qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr reciveData error! "; @@ -310,10 +283,47 @@ void Logic_myiothread::run() } } } + //程序号;传到到主界面类,再进行切换 + if (!program_Num.isEmpty()) + { + QString m_startpos = program_Num;// .split("_")[0]; + //QString m_value = program_Num.split("_")[1]; + int trigger_index = m_startpos.mid(1, m_startpos.length() - 1).toInt(); + trigger_index -= trigger_startpos; + if (trigger_index < 0) + { + qCritical() << __FUNCTION__ << " line:" << __LINE__ << " vec_value is error! read_sn_startpos:" << m_startpos; + } + else if (vec_value.size() > trigger_index) + { + //读复位标志满足 + if (vec_value.at(trigger_index) != 0) + { + qCritical() << __FUNCTION__ << " line:" << __LINE__ << " 程序号:" << vec_value.at(trigger_index); + + dataPack m_data; + m_data.algorithmName = "程序号"; + m_data.cameraName = m_startpos; + m_data.byteArray = QByteArray::number(vec_value.at(trigger_index)); + m_data.retFlag = "algorithm"; + + QMutexLocker locker(sendProcess_mutex); + sendProcessTask.enqueue(m_data); + //完成清标志 + QByteArray send_byte = program_Num_back.toUtf8(); + if (!finstcp_ptr->writeData(vec_mat, send_byte)) + { + qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr reciveData error! "; + return; + } + } + } + } } } } } + // 写入 writeCommond(); @@ -362,8 +372,8 @@ void Logic_myiothread::reciveCommond() // 修改读取多个命令 //QMutexLocker locker ( m_iomutex ); m_iomutex->lock(); - QByteArray byteArray = readAll_cmd.toUtf8(); // 设置 从起始位开始读取的指令 - if ( !finstcp_ptr->writeData(vec_mat,byteArray) ) + QByteArray byte = readAll_cmd.toUtf8(); // 设置 从起始位开始读取的指令 + if ( !finstcp_ptr->writeData(vec_mat,byte) ) { qCritical() << __FUNCTION__ << "line:"<< __LINE__ << " finstcp_ptr reciveData error!"; } @@ -378,7 +388,7 @@ void Logic_myiothread::reciveCommond() stop = clock(); double ctime = stop - start; //if (ctime > 500) - // qCritical() << __FUNCTION__ << __LINE__ << "readAll_cmd ctime :" << ctime;//;127ms + //qCritical() << __FUNCTION__ << __LINE__ << "readAll_cmd ctime :" << ctime<<" m_stationName:"< vec_value, QByteArray byteArray) +void Logic_myiothread::readSnInfo(QVector vec_value, QByteArray byte) { QStringList keys = map_sn_area_array.keys(); //重新加载用 @@ -497,19 +507,19 @@ void Logic_myiothread::readSnInfo(QVector vec_value, QByteArray byteArray) // sn码防重入检查 QVector m_vec_sn_flag = map_read_sn_reentry_vector_flag.value(station_name); - if (m_vec_sn_flag.at(channel_index)) // false 添加sn码 true 已经添加过sn,不再重复添加 - { - // 清理当前sn码的触发信号 - QString m_signal = "write_short_" + m_startpos + "_0"; - QByteArray m_byte = m_signal.toUtf8(); - QMutexLocker locker(m_iomutex); - if (!finstcp_ptr->writeData(vec_mat, m_byte)) - { - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; - return; - } - continue; - } + //if (m_vec_sn_flag.at(channel_index)) // false 添加sn码 true 已经添加过sn,不再重复添加 + //{ + // // 清理当前sn码的触发信号 + // QString m_signal = "write_short_" + m_startpos + "_0"; + // QByteArray m_byte = m_signal.toUtf8(); + // //QMutexLocker locker(m_iomutex);; + // if (!finstcp_ptr->writeData(vec_mat, m_byte)) + // { + // qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; + // return; + // } + // continue; + //} qCritical() << __FUNCTION__ << " line:" << __LINE__ << " m_vec_sn_flag:" << m_vec_sn_flag.at(channel_index); @@ -566,7 +576,7 @@ void Logic_myiothread::readSnInfo(QVector vec_value, QByteArray byteArray) for (int aaa = 0; aaa < read_datatime_list.size(); aaa++) { //SN为""时,返回配置文件的read_sn_error_send_format值 - QString datatime = getSn(read_sn_error_send_format, read_datatime_list[aaa].toString(), byteArray); + QString datatime = getSn(read_sn_error_send_format, read_datatime_list[aaa].toString(), byte); qDebug() << __FUNCTION__ << " line: " << __LINE__ << " channel_index: " << channel_index << " read_datatime_list: " << datatime; //sn_value = snHex(sn_value); // sn方向相反,重新做转换 @@ -638,7 +648,7 @@ void Logic_myiothread::readSnInfo(QVector vec_value, QByteArray byteArray) for (int aaa = 0; aaa < read_sn_list.size(); aaa++) { //SN为""时,返回配置文件的read_sn_error_send_format值 - QString sn_value = getSn(read_sn_error_send_format, read_sn_list[aaa].toString(), byteArray); + QString sn_value = getSn(read_sn_error_send_format, read_sn_list[aaa].toString(), byte); qDebug() << __FUNCTION__ << " line: " << __LINE__ << " channel_index: " << channel_index << " sn_value: " << sn_value; //sn_value = snHex(sn_value); // sn方向相反,重新做转换 //异常 @@ -668,35 +678,72 @@ void Logic_myiothread::readSnInfo(QVector vec_value, QByteArray byteArray) - // 清理当前区域数据 - QString clear_SN = obj.value("读SN后-清0").toString(); - if (!clear_SN.isEmpty()) + //// 清理当前区域数据 + //QString clear_SN = obj.value("读SN后-清0").toString(); + //if (!clear_SN.isEmpty()) + //{ + // QStringList mycmdHead = clear_SN.split("_"); + // for (int aaa = 0; aaa < read_sn_list.size(); aaa++) + // { + // QString sn_area = read_sn_list.at(aaa).toString(); + // QString start_pos = sn_area.split("_")[0]; + // int lenght = sn_area.split("_")[1].toInt(); + // QString m_signal = nullptr; + // //SN清空 + // for (auto yyy = 0; yyy < (lenght+1)/2; yyy++) + // { + // m_signal += mycmdHead[2]; + // if (yyy != ((lenght + 1) / 2 - 1)) + // m_signal += ","; + // } + // m_signal = mycmdHead[0] + "_" + mycmdHead[1] + "_" + start_pos + "_" + m_signal;// QString("write_short_%1_%2").arg(start_pos).arg(m_signal); + // QByteArray m_byte = m_signal.toUtf8(); + // //QMutexLocker locker(m_iomutex);; + // if (!finstcp_ptr->writeData(vec_mat, m_byte)) + // { + // qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; + // return; + // } + // } + //} + + if (ifReadSnErr == true) { - QStringList mycmdHead = clear_SN.split("_"); - for (int aaa = 0; aaa < read_sn_list.size(); aaa++) + QStringList snOKCmdList = obj.value("读SN异常-回复指令").toString().split("#"); + if (!snOKCmdList.isEmpty()) { - QString sn_area = read_sn_list.at(aaa).toString(); - QString start_pos = sn_area.split("_")[0]; - int lenght = sn_area.split("_")[1].toInt(); - QString m_signal = nullptr; - //SN清空 - for (auto yyy = 0; yyy < (lenght+1)/2; yyy++) + for (int s = 0; s < snOKCmdList.size(); s++) { - m_signal += mycmdHead[2]; - if (yyy != ((lenght + 1) / 2 - 1)) - m_signal += ","; - } - m_signal = mycmdHead[0] + "_" + mycmdHead[1] + "_" + start_pos + "_" + m_signal;// QString("write_short_%1_%2").arg(start_pos).arg(m_signal); - QByteArray m_byte = m_signal.toUtf8(); - QMutexLocker locker(m_iomutex); - if (!finstcp_ptr->writeData(vec_mat, m_byte)) - { - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; - return; + //snOKCmd = CmdHeadShort + snOKCmd; + QByteArray ok_byte = snOKCmdList[s].toUtf8(); + if (!finstcp_ptr->writeData(vec_mat, ok_byte)) + { + qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; + return; + } + } } } - + else + { + QStringList snOKCmdList = obj.value("读SN正常-回复指令").toString().split("#"); + if (!snOKCmdList.isEmpty()) + { + for (int s = 0; s < snOKCmdList.size(); s++) + { + //snOKCmd = CmdHeadShort + snOKCmd; + QByteArray ok_byte = snOKCmdList[s].toUtf8(); + if (!finstcp_ptr->writeData(vec_mat, ok_byte)) + { + qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; + return; + } + + } + } + } + // 清理当前sn码的触发信号 QString m_signal = "write_short_" + m_startpos + "_0"; QByteArray m_byte = m_signal.toUtf8(); @@ -706,38 +753,6 @@ void Logic_myiothread::readSnInfo(QVector vec_value, QByteArray byteArray) qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; return; } - - - - if (ifReadSnErr == true) - { - QString snErrCmd = obj.value("读SN异常-回复指令").toString(); - if (!snErrCmd.isEmpty()) - { - //snErrCmd = CmdHeadShort + snErrCmd; - QByteArray ok_byte = snErrCmd.toUtf8(); - if (!finstcp_ptr->writeData(vec_mat, ok_byte)) - { - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; - return; - } - } - } - else - { - // snOKCmd - QString snOKCmd = obj.value("读SN正常-回复指令").toString(); - if (!snOKCmd.isEmpty()) - { - //snOKCmd = CmdHeadShort + snOKCmd; - QByteArray ok_byte = snOKCmd.toUtf8(); - if (!finstcp_ptr->writeData(vec_mat, ok_byte)) - { - qCritical() << __FUNCTION__ << " line:" << __LINE__ << " finstcp_ptr reciveData error! "; - return; - } - } - } } else if ( vec_value.at(trigger_index) == 0 ){ //读到PLC标志为0时 // 更改防重入标识符 @@ -873,7 +888,7 @@ QString Logic_myiothread::snHex(QString sn) return resultStr; } //SN为""时,返回配置文件的"读SN异常-插值" -QString Logic_myiothread::getSn(QString errorinfo,QString area,QByteArray byteArray) +QString Logic_myiothread::getSn(QString errorinfo,QString area,QByteArray byte) { QString m_startpos = area.split("_")[0]; QString lenght = area.split("_")[1]; @@ -881,7 +896,7 @@ QString Logic_myiothread::getSn(QString errorinfo,QString area,QByteArray byteAr int start_index = m_startpos.mid(1,m_startpos.length()-1).toInt(); start_index -= trigger_startpos; - QByteArray snByte = byteArray.mid(start_index*2,lenght.toInt()); + QByteArray snByte = byte.mid(start_index*2,lenght.toInt()); QString sn = snByte; if ( sn.contains("\n\r") ) @@ -1082,14 +1097,14 @@ void Logic_myiothread::checkTriggerCamera(QVector vec_value) QStringList myCmdHead = m_pack.clear_camera_startpos_flag.split("_"); clear_signal = myCmdHead[0]+"_"+ myCmdHead[1] +"_" + clear_signal + +"_" + myCmdHead[2]; QByteArray m_byte = clear_signal.toUtf8(); - QMutexLocker locker(m_iomutex); - if ( !finstcp_ptr->writeData(vec_mat,m_byte) ) + ////QMutexLocker locker(m_iomutex);; + /*if ( !finstcp_ptr->writeData(vec_mat,m_byte) ) { qCritical() << __FUNCTION__ << " line:"<< __LINE__ << " finstcp_ptr reciveData error! "; return; - } + }*/ } - if ( vec_value.at(trigger_index) == m_value.toInt() && m_pack.trigger_reentry_signal == 0 ) + if ( vec_value.at(trigger_index) == m_value.toInt() )//&& m_pack.trigger_reentry_signal == 0 ) { qDebug() << __FUNCTION__ << " line:" << __LINE__ << "trigger_signal: " << trigger_signal; @@ -1189,7 +1204,7 @@ void Logic_myiothread::checkTriggerAlgorithm(QVector vec_value) } } if (vec_value.at(trigger_index) == m_value.toInt() ) - if(m_pack.trigger_reentry_signal == 0) + //if(m_pack.trigger_reentry_signal == 0) { qDebug() << __FUNCTION__ << " line:" << __LINE__ << "trigger_signal: " << trigger_signal; @@ -1258,8 +1273,8 @@ void Logic_myiothread::checkTriggerAlgorithm(QVector vec_value) //} } - else - qDebug() << __FUNCTION__ << " line:" << __LINE__ << "trigger_signal: " << trigger_signal << " m_pack.trigger_reentry_signal != 0"; + //else + // qDebug() << __FUNCTION__ << " line:" << __LINE__ << "trigger_signal: " << trigger_signal << " m_pack.trigger_reentry_signal != 0"; } } } @@ -1741,7 +1756,7 @@ QByteArray runAndCheck_cameraThread::checkCamerasFinish_feedbackSignal(trigger_s QByteArray send_byte = send_msg.toUtf8(); return send_byte; - /*QMutexLocker locker(m_iomutex); + /*//QMutexLocker locker(m_iomutex);; if (!finstcp_ptr->writeData(vec_mat, send_byte)) { qCritical() << __FUNCTION__ << "line:" << __LINE__ << " finstcp_ptr reciveData error! "; diff --git a/BaseModules/Hd_LogicDll_module/Logic_myiothread.h b/BaseModules/Hd_LogicDll_module/Logic_myiothread.h index b3b38ef..e7b83e9 100644 --- a/BaseModules/Hd_LogicDll_module/Logic_myiothread.h +++ b/BaseModules/Hd_LogicDll_module/Logic_myiothread.h @@ -181,6 +181,7 @@ private: QQueue< dataPack> sendProcessTask; QByteArray readsnByte ; QVector vec_value; + clock_t heart_start,heart_stop; signals: }; diff --git a/configBackup/HanddleAI.exe b/configBackup/HanddleAI.exe new file mode 100644 index 0000000..444571c Binary files /dev/null and b/configBackup/HanddleAI.exe differ diff --git a/doc/三协地址表/一次注液地址表.xlsx b/doc/三协地址表/一次注液地址表.xlsx index 0b8ffe2..49e5d41 100644 Binary files a/doc/三协地址表/一次注液地址表.xlsx and b/doc/三协地址表/一次注液地址表.xlsx differ diff --git a/doc/三协地址表/软连接3-2地址表.xlsx b/doc/三协地址表/软连接3-2地址表.xlsx index 557423f..9eb8544 100644 Binary files a/doc/三协地址表/软连接3-2地址表.xlsx and b/doc/三协地址表/软连接3-2地址表.xlsx differ