diff --git a/AlgorithmModule/include/ImageAllResult.h b/AlgorithmModule/include/ImageAllResult.h index a6e59b2..ff2d351 100644 --- a/AlgorithmModule/include/ImageAllResult.h +++ b/AlgorithmModule/include/ImageAllResult.h @@ -44,6 +44,13 @@ public: cv::Mat AI_mask; }; + // 单个检测模型输出的 全图 mask,用于追溯 残点(blob) 由哪个模型检出 + struct AI_Model_MaskImg + { + std::string strModelName; // 模型名称 + cv::Mat ModelMask; // 该模型输出的 全图 mask + }; + private: /* data */ public: @@ -87,6 +94,8 @@ public: std::shared_ptr cameraBaseResult; // 相机相关的基本结果信息 std::vector> AI_Qx_MaskList; + // 各检测模型独立的全图 mask 列表(与检测模型一一对应) + std::vector> AI_Model_MaskList; std::mutex mtx_Det; // 互斥量 private: PRINT_LOG_ m_PrintLog; diff --git a/AlgorithmModule/include/ImageDetConfig.h b/AlgorithmModule/include/ImageDetConfig.h index 3e9acd1..b6f1146 100644 --- a/AlgorithmModule/include/ImageDetConfig.h +++ b/AlgorithmModule/include/ImageDetConfig.h @@ -27,6 +27,7 @@ struct QX_ERROR_INFO_ float fbreadth; int nconfig_qx_type; std::string qx_name; + std::string strModelName; // 检出该残点(blob)的模型名称 int maxValue; int whiteOrBlack; float grayDis; @@ -53,6 +54,7 @@ struct QX_ERROR_INFO_ flen = 0; nconfig_qx_type = 0; qx_name = ""; + strModelName = ""; maxValue = 0; grayDis = 0; fUpIou = 0; @@ -79,6 +81,7 @@ struct QX_ERROR_INFO_ << ", Flen: " << flen << ", nconfig_qx_type: " << nconfig_qx_type << ", QX Name: " << qx_name + << ", Model Name: " << strModelName << ", Max Value: " << maxValue << ", Gray Dis: " << grayDis << ", fUpIou: " << fUpIou diff --git a/AlgorithmModule/include/ImgCheckAnalysisy.hpp b/AlgorithmModule/include/ImgCheckAnalysisy.hpp index 0df439f..b9b62c4 100644 --- a/AlgorithmModule/include/ImgCheckAnalysisy.hpp +++ b/AlgorithmModule/include/ImgCheckAnalysisy.hpp @@ -198,6 +198,9 @@ private: // 把blob 汇总成 检测结果。 int BLobToDetResult(); + // 获取指定 残点(blob) 是由哪个(哪些)检测模型检出的 + std::string GetBlobModelName(const cv::Rect &roi); + //自适应参数区域更新 int Adapt_Config(cv::RotatedRect tplOuterRect, cv::RotatedRect curOuterRect, cv::Mat& det_img); diff --git a/AlgorithmModule/include/ImgCheckConfig.h b/AlgorithmModule/include/ImgCheckConfig.h index f27992a..e9471a7 100644 --- a/AlgorithmModule/include/ImgCheckConfig.h +++ b/AlgorithmModule/include/ImgCheckConfig.h @@ -270,6 +270,13 @@ struct DetectInfo } }; +// 残点检测模型名称:用于区分残点由哪个模型检出(存图分文件夹 + 日志追溯) +#define AI_DET_MODEL_NAME_Defect "Defect" +#define AI_DET_MODEL_NAME_QueXi "Defect_QueXi" +#define AI_DET_MODEL_NAME_UnKnown "UnKnown" +// 多个模型都检出同一个残点时的连接符 +#define AI_DET_MODEL_NAME_JOIN "+" + struct QXImageResult { int type; // 缺陷类型 diff --git a/AlgorithmModule/src/CheckResultJson.cpp b/AlgorithmModule/src/CheckResultJson.cpp index 3ffd12d..3b86911 100644 --- a/AlgorithmModule/src/CheckResultJson.cpp +++ b/AlgorithmModule/src/CheckResultJson.cpp @@ -38,6 +38,7 @@ Json::Value CheckResultJson::toJsonValue() value["flen"] = p->flen; value["nconfig_qx_type"] = p->nconfig_qx_type; value["qx_name"] = p->qx_name; + value["det_model_name"] = p->strModelName; value["maxValue"] = p->maxValue; value["grayDis"] = p->grayDis; value["fUpIou"] = p->fUpIou; @@ -117,6 +118,11 @@ void CheckResultJson::toObjectFromValue(Json::Value root) tem.flen = errorJson["flen"].asFloat(); tem.nconfig_qx_type = errorJson["nconfig_qx_type"].asInt(); tem.qx_name = errorJson["qx_name"].asString(); + // 旧版本 json 可能没有该字段 + if (errorJson.isMember("det_model_name")) + { + tem.strModelName = errorJson["det_model_name"].asString(); + } tem.maxValue = errorJson["maxValue"].asInt(); tem.grayDis = errorJson["grayDis"].asFloat(); tem.fUpIou = errorJson["fUpIou"].asFloat(); diff --git a/AlgorithmModule/src/ImageResultJudge.cpp b/AlgorithmModule/src/ImageResultJudge.cpp index a3df8f2..e951f26 100644 --- a/AlgorithmModule/src/ImageResultJudge.cpp +++ b/AlgorithmModule/src/ImageResultJudge.cpp @@ -173,9 +173,9 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) std::string qx_wb = WHITE_BLCAK_Names[QX_whiteBLACK]; pQxLog->AddCheckstr(PrintLevel_1, - "", "qxidx: %d roi[%d %d %d %d] qx %d %s %s JudgArea %f hj %f len %f breadth %f energy %d region num %ld", + "", "qxidx: %d roi[%d %d %d %d] qx %d %s %s JudgArea %f hj %f len %f breadth %f energy %d region num %ld detModel: %s", qxidx, roi.x, roi.y, roi.width, roi.height, config_qx_type, qx_name.c_str(), qx_wb.c_str(), - JudgArea, grayDis, flen, fbreadth, energy, QX_info->detRegionidxList.size()); + JudgArea, grayDis, flen, fbreadth, energy, QX_info->detRegionidxList.size(), QX_info->strModelName.c_str()); cv::Point pCenter; pCenter.x = roi.x + roi.width * 0.5; @@ -559,7 +559,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); m_CheckResult_shareP->qxImageResult.push_back(tem); - pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, " result ", " ---name: %s ---code: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), tem.srcImgroi.x, tem.srcImgroi.y); + pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, " result ", " ---name: %s ---code: %s ---detModel: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), QX_info->strModelName.c_str(), tem.srcImgroi.x, tem.srcImgroi.y); } else { @@ -572,6 +572,8 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); m_CheckResult_shareP->YS_ImageResult.push_back(tem); + // 疑似小图 也需要能追溯 是哪个模型检出的残点 + pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, " YS result ", " ---name: %s ---code: %s ---detModel: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), QX_info->strModelName.c_str(), tem.srcImgroi.x, tem.srcImgroi.y); } } } @@ -673,6 +675,8 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); m_CheckResult_shareP->qxImageResult.push_back(tem); + // YS 转 NG 的残点也需要能追溯 是哪个模型检出的 + pQxLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, " YS to NG img ", " ---name: %s ---code: %s ---detModel: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), QX_info->strModelName.c_str(), tem.srcImgroi.x, tem.srcImgroi.y); } } } diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 0d0ef8d..95c6ffe 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -1346,6 +1346,11 @@ int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg) pAIDetList.push_back(AI_Factory->Defect); pAIDetList.push_back(AI_Factory->Defect_QueXi); + // 与 pAIDetList 一一对应的模型名称,用于区分残点由哪个模型检出 + std::vector pAIDetNameList; + pAIDetNameList.push_back(AI_DET_MODEL_NAME_Defect); + pAIDetNameList.push_back(AI_DET_MODEL_NAME_QueXi); + std::string strBaseLog = "AI_Detect"; m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "AI_Detect Start"); @@ -1396,6 +1401,14 @@ int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg) ResultImg = cv::Mat::zeros(img.size(), CV_8UC1); + // 每个模型单独保存一份全图 mask,便于后续追溯残点由哪个模型检出 + m_pImageAllResult->AI_Model_MaskList.clear(); + std::vector modelMaskList(pAIDetList.size()); + for (size_t m = 0; m < pAIDetList.size(); m++) + { + modelMaskList[m] = cv::Mat::zeros(img.size(), CV_8UC1); + } + const int totalTasks = SmallRoiList.size(); // std::string str_Root = m_strRootPath + pDetConfig->strProductID + "_" + pDetConfig->strchannel + "_"; @@ -1440,6 +1453,28 @@ int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg) // 多个模型的结果取并集 cv::Mat roiMat = ResultImg(result->roi); cv::bitwise_or(roiMat, outimg, roiMat); + + // 当前模型自己的 mask 单独记录一份 + if (m < modelMaskList.size() && !modelMaskList[m].empty()) + { + cv::Mat modelRoiMat = modelMaskList[m](result->roi); + cv::bitwise_or(modelRoiMat, outimg, modelRoiMat); + } + + // AI 检测残点图存图:每个模型的结果都存,并增加一级 模型名称 目录, + // 用以区分该残点是由哪个模型检出的 + // 目录结构:/home/aidlux/BOE/ZB_SOCKET/AI_Detect/<模型名称>/<产品ID>/ + if (m_pbaseCheckFunction->saveImg.bSaveAIDetImg) + { + std::string strModelName = (m < pAIDetNameList.size()) ? pAIDetNameList[m] : std::string(AI_DET_MODEL_NAME_UnKnown); + std::string roi_ext = "[" + std::to_string(result->roi.x) + "," + std::to_string(result->roi.y) + "]"; + std::string strSaveDir = "/home/aidlux/BOE/ZB_SOCKET/AI_Detect/" + strModelName + "/" + DetImgInfo_shareP->strImgProductID + "/"; + CheckUtil::CreateDir(strSaveDir); + std::string strSavePath_in = strSaveDir + m_strCurDetChannel + "_" + roi_ext + "_in.png"; + std::string strSavePath_out = strSaveDir + m_strCurDetChannel + "_" + roi_ext + "_out.png"; + cv::imwrite(strSavePath_in, result->input); + cv::imwrite(strSavePath_out, outimg); + } } // 同一 roi 的多个模型结果取并集后存入 AI_Qx_MaskList @@ -1474,6 +1509,18 @@ int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg) float mean_AI = (t3 - t2) / (SmallRoiList.size() * pAIDetList.size()); m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strBaseLog, " AI Run Time: sum %ld pre %ld Run %ld mean One Small Img %f", t3 - t1, t2 - t1, t3 - t2, mean_AI); + // 保存各模型的 全图 mask,并输出各模型检出的残点像素数量,便于追溯 + for (size_t m = 0; m < modelMaskList.size(); m++) + { + std::shared_ptr temModelMask = std::make_shared(); + temModelMask->strModelName = (m < pAIDetNameList.size()) ? pAIDetNameList[m] : std::string(AI_DET_MODEL_NAME_UnKnown); + temModelMask->ModelMask = modelMaskList[m]; + m_pImageAllResult->AI_Model_MaskList.push_back(temModelMask); + + m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, " Det Model: %s mask pixel num %d", + temModelMask->strModelName.c_str(), cv::countNonZero(temModelMask->ModelMask)); + } + m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "AI_Detect End"); // 临时存图 @@ -2058,6 +2105,53 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img) return 0; } +// 获取指定 残点(blob) 是由哪个(哪些)检测模型检出的 +// 做法:对各模型自己输出的全图 mask,在 blob 的 roi 内统计非零像素; +// 非零则说明该模型在该残点区域有检出。多个模型都检出时用 "+" 连接。 +std::string ImgCheckAnalysisy::GetBlobModelName(const cv::Rect &roi) +{ + std::string strModelName = ""; + if (!m_pImageAllResult) + { + return AI_DET_MODEL_NAME_UnKnown; + } + + for (auto &pModelMask : m_pImageAllResult->AI_Model_MaskList) + { + if (!pModelMask || pModelMask->ModelMask.empty()) + { + continue; + } + + cv::Rect imgRoi = cv::Rect(0, 0, pModelMask->ModelMask.cols, pModelMask->ModelMask.rows); + cv::Rect cutRoi = roi & imgRoi; + if (cutRoi.width <= 0 || cutRoi.height <= 0) + { + continue; + } + + cv::Mat roiMask = pModelMask->ModelMask(cutRoi); + if (cv::countNonZero(roiMask) > 0) + { + if (strModelName.empty()) + { + strModelName = pModelMask->strModelName; + } + else + { + strModelName += AI_DET_MODEL_NAME_JOIN + pModelMask->strModelName; + } + } + } + + if (strModelName.empty()) + { + strModelName = AI_DET_MODEL_NAME_UnKnown; + } + + return strModelName; +} + int ImgCheckAnalysisy::BLobToDetResult() { long t1 = CheckUtil::getcurTime(); @@ -2078,6 +2172,9 @@ int ImgCheckAnalysisy::BLobToDetResult() float fsecondArea = JudgArea; std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type]; + // 追溯该残点(blob) 是由哪个检测模型检出的 + std::string strModelName = GetBlobModelName(roi); + if (true) { QX_ERROR_INFO_ temerror; @@ -2091,6 +2188,7 @@ int ImgCheckAnalysisy::BLobToDetResult() temerror.fbreadth = blobs.blobTab[i].breadth; temerror.nconfig_qx_type = config_qx_type; temerror.qx_name = qx_name; + temerror.strModelName = strModelName; temerror.maxValue = blobs.blobTab[i].maxValue; temerror.grayDis = blobs.blobTab[i].grayDis; temerror.density = blobs.blobTab[i].density; @@ -2138,6 +2236,11 @@ int ImgCheckAnalysisy::BLobToDetResult() m_pDetResult->pQx_ErrorList->push_back(temerror); + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "BLobToDetResult", + "blob idx %d Qx Idx %d qx %d %s detModel %s roi[%d %d %d %d]", + i, temerror.Idx, config_qx_type, qx_name.c_str(), strModelName.c_str(), + roi.x, roi.y, roi.width, roi.height); + // printf("- %s idx %d a %f v %d h %f l %f\n", DetImgInfo_shareP->strChannel.c_str(), i, JudgArea, blobs.blobTab[i].maxValue, blobs.blobTab[i].grayDis, flen); } } diff --git a/ConfigModule/include/CheckConfigDefine.h b/ConfigModule/include/CheckConfigDefine.h index 956c90b..0cc5dfd 100644 --- a/ConfigModule/include/CheckConfigDefine.h +++ b/ConfigModule/include/CheckConfigDefine.h @@ -1063,6 +1063,7 @@ struct Base_Function_SaveImg bool bSaveMarkImg; // mark 存图 bool bSaveClsImg; // 分类 存图 bool bSaveAlginImg; // 分类 存图 + bool bSaveAIDetImg; // AI 检测残点图 存图(每个模型单独一级目录) Base_Function_SaveImg() { @@ -1074,6 +1075,7 @@ struct Base_Function_SaveImg bSaveMarkImg = false; bSaveClsImg = false; bSaveAlginImg = false; + bSaveAIDetImg = false; } void copy(Base_Function_SaveImg tem) @@ -1083,17 +1085,18 @@ struct Base_Function_SaveImg this->bSaveMarkImg = tem.bSaveMarkImg; this->bSaveClsImg = tem.bSaveClsImg; this->bSaveAlginImg = tem.bSaveAlginImg; + this->bSaveAIDetImg = tem.bSaveAIDetImg; } void print(std::string str) { - printf("%s>>bOpen %d bSaveMarkImg %d bSaveClsImg %d bSaveAlginImg %d \n", str.c_str(), - bOpen, bSaveMarkImg, bSaveClsImg, bSaveAlginImg); + printf("%s>>bOpen %d bSaveMarkImg %d bSaveClsImg %d bSaveAlginImg %d bSaveAIDetImg %d \n", str.c_str(), + bOpen, bSaveMarkImg, bSaveClsImg, bSaveAlginImg, bSaveAIDetImg); } std::string GetInfo(std::string str) { char buffer[256]; - sprintf(buffer, "%s>>bOpen %d bSaveMarkImg %d bSaveClsImg %d bSaveAlginImg %d \n", str.c_str(), - bOpen, bSaveMarkImg, bSaveClsImg, bSaveAlginImg); + sprintf(buffer, "%s>>bOpen %d bSaveMarkImg %d bSaveClsImg %d bSaveAlginImg %d bSaveAIDetImg %d \n", str.c_str(), + bOpen, bSaveMarkImg, bSaveClsImg, bSaveAlginImg, bSaveAIDetImg); std::string str123 = buffer; return str123; } diff --git a/ConfigModule/src/JsonConfig.cpp b/ConfigModule/src/JsonConfig.cpp index 56c95c8..fcab0af 100644 --- a/ConfigModule/src/JsonConfig.cpp +++ b/ConfigModule/src/JsonConfig.cpp @@ -846,6 +846,10 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value) { _config.saveImg.bSaveAlginImg = value_f["form"]["SaveImg_Config"]["Align_Big_Save"].asBool(); } + if (value_f["form"]["SaveImg_Config"]["AI_Det_Save"]) + { + _config.saveImg.bSaveAIDetImg = value_f["form"]["SaveImg_Config"]["AI_Det_Save"].asBool(); + } } else {