From a9ec94a936153f4c66a55f23889c6044860f41bc Mon Sep 17 00:00:00 2001 From: liusiyang Date: Tue, 13 Oct 2026 21:03:38 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E5=BC=80=E6=94=BE=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E6=98=A0=E5=B0=84=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AlgorithmModule/src/ImgCheckAnalysisy.cpp | 194 +++++++++++++++++++--- 1 file changed, 174 insertions(+), 20 deletions(-) diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 57de6e6..5c49dae 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -14,6 +14,9 @@ #include "AlignShare.h" #include #include +#include +#include +#include // AI缺陷分类模型开关:当前暂无分类模型(Class.engine),分类流程代码先预留; // 待分类模型部署后,将 AI_CLASS_MODEL_ENABLE 置 1,即可恢复 AI_QX_Class_Thread 中的AI分类流程 @@ -27,6 +30,165 @@ #define HANDLE_NEAR_EXPAND_RATIO 5 // 把手缺失检测开关见 Base层 Function_Support_Det::bDetHandle(CheckConfigDefine.h) +// 分类映射配置文件(每行:label索引:缺陷名称;冒号后名称为该缺陷类型名称,行的顺序即缺陷类型索引) +static const std::string CLS_LABEL_CONFIG_FILE = "/home/aidlux/BOE/UseModel_ZB/BM_cls.txt"; + +// 去掉字符串首尾的空白字符和引号 +static std::string ClsTrimStr(const std::string &str) +{ + size_t begin = str.find_first_not_of(" \t\r\n\""); + if (begin == std::string::npos) + { + return ""; + } + size_t end = str.find_last_not_of(" \t\r\n\""); + return str.substr(begin, end - begin + 1); +} + +// 组装程序内默认缺陷名称列表(AI_CLass_QX_NAME_Names) +static std::vector BuildDefaultAIQXNames() +{ + std::vector vecNames; + for (int i = 0; i < AI_CLass_QX_NAME_count; i++) + { + vecNames.push_back(AI_CLass_QX_NAME_Names[i]); + } + return vecNames; +} + +// 分类映射配置:缺陷名称列表(索引=行序) + 分类label->缺陷名称 映射 +struct ClsLabelConfig +{ + std::vector vecNames; // 缺陷名称列表 + std::map mapLabelName; // 分类 label -> 缺陷名称 +}; + +// 读取分类映射配置(每行:label索引:缺陷名称;冒号后名称按行序构成缺陷名称列表;# 后内容忽略) +static ClsLabelConfig ReadClsLabelConfig(const std::string &strFilePath) +{ + ClsLabelConfig config; + std::ifstream ifs(strFilePath.c_str()); + if (!ifs.is_open()) + { + printf("Open cls label config failed, use default mapping: %s\n", strFilePath.c_str()); + return config; + } + + std::string strLine; + while (std::getline(ifs, strLine)) + { + // # 后内容(含注释)忽略 + size_t nPosComment = strLine.find('#'); + if (nPosComment != std::string::npos) + { + strLine = strLine.substr(0, nPosComment); + } + size_t nPos = strLine.find(':'); + if (nPos == std::string::npos) + { + // 无冒号:非标准行,跳过并提示(仅支持 label索引:缺陷名称 格式) + std::string strTrim = ClsTrimStr(strLine); + if (!strTrim.empty()) + { + printf("Cls label config skip invalid line: %s\n", strTrim.c_str()); + } + continue; + } + // label索引:缺陷名称(冒号后名称按行序构成缺陷名称列表) + std::string strKey = ClsTrimStr(strLine.substr(0, nPos)); + std::string strValue = ClsTrimStr(strLine.substr(nPos + 1)); + if (!strKey.empty() && !strValue.empty()) + { + config.mapLabelName[atoi(strKey.c_str())] = strValue; + config.vecNames.push_back(strValue); // 行序即缺陷类型索引 + } + } + return config; +} + +// 获取分类映射配置(只读一次) +static const ClsLabelConfig &GetClsLabelConfig() +{ + static const ClsLabelConfig config = ReadClsLabelConfig(CLS_LABEL_CONFIG_FILE); + return config; +} + +// 获取缺陷名称列表:优先取配置文件的名称列表,读不到用程序内默认列表 +static const std::vector &GetAIQXNameList() +{ + static const std::vector vecDefaultNames = BuildDefaultAIQXNames(); + const ClsLabelConfig &config = GetClsLabelConfig(); + if (!config.vecNames.empty()) + { + return config.vecNames; + } + return vecDefaultNames; +} + +// 缺陷名称 -> AI_CLass 枚举(找不到返回 count 表示无效) +static int ClsNameToAItype(const std::string &strName) +{ + const std::vector &vecNames = GetAIQXNameList(); + for (size_t i = 0; i < vecNames.size(); i++) + { + if (strName == vecNames[i]) + { + return (int)i; + } + } + return AI_CLass_QX_NAME_count; +} + +// 解析 label->缺陷名称 映射为 label->AI_CLass枚举(只读一次;无效名称仅提示一次) +static std::map BuildLabelTypeMap() +{ + const ClsLabelConfig &config = GetClsLabelConfig(); + std::map mapLabelType; + for (std::map::const_iterator it = config.mapLabelName.begin(); it != config.mapLabelName.end(); ++it) + { + int nType = ClsNameToAItype(it->second); + if (nType < AI_CLass_QX_NAME_count) + { + mapLabelType[it->first] = nType; + } + else + { + printf("Cls label config unknown name: %s\n", it->second.c_str()); + } + } + return mapLabelType; +} + +// 获取分类 label 对应的 AI 缺陷类型:优先配置文件(只读一次),读不到用默认顺序 +static int GetClsTypeByLabel(int nLabel) +{ + static const std::map mapLabelType = BuildLabelTypeMap(); + std::map::const_iterator it = mapLabelType.find(nLabel); + if (it != mapLabelType.end()) + { + return it->second; + } + + // 映射文件未读到、或该 label 未在文件中配置时,按程序内默认顺序兜底(避免文件缺失/不全导致异常) + int cls_num = AI_CLass_QX_NAME_count; + switch (nLabel) + { + case 0: + cls_num = AI_CLass_QX_NAME_CGMark; + break; + case 1: + cls_num = AI_CLass_QX_NAME_BQBMark; + break; + case 2: + cls_num = AI_CLass_QX_NAME_TBMark; + break; + default: + cls_num = AI_CLass_QX_NAME_CGMark; + break; + } + return cls_num; +} + // 用于排序轮廓的比较函数 static bool compareContourAreas(const vector &contour1, const vector &contour2) { @@ -2165,27 +2327,9 @@ int ImgCheckAnalysisy::AI_QX_Class_Thread() { int temClass = result->cls_label; - int cls_num = 0; - - // 分类模型label顺序:0-把手标记(CGMark) 1-补强板标记(BQBMark) 2-铜箔标记(TBMark) - switch (temClass) - { - case 0: - cls_num = AI_CLass_QX_NAME_CGMark; - break; - case 1: - cls_num = AI_CLass_QX_NAME_BQBMark; - break; - - case 2: - cls_num = AI_CLass_QX_NAME_TBMark; - break; - - default: - cls_num = AI_CLass_QX_NAME_CGMark; - break; - } + // 分类 label 映射:优先读映射配置文件(BM_cls.txt),读不到用默认顺序 + int cls_num = GetClsTypeByLabel(temClass); // std::string strclassName = AI_CLass_QX_NAME_Names[cls_num]; // printf("AI Class num %d = %s %f\n", cls_num, strclassName.c_str(), result->cls_score); @@ -2480,6 +2624,9 @@ int ImgCheckAnalysisy::AIClassTypeToConfigType(int nAIQXType) case AI_CLass_QX_NAME_TBMark: resultError_type = CONFIG_QX_NAME_cell_TBMark; break; + case AI_CLass_QX_NAME_count: // 无效类别(分类映射配置中不存在):不参与缺陷分析 + resultError_type = CONFIG_QX_NAME_count; + break; default: break; } @@ -2516,6 +2663,13 @@ int ImgCheckAnalysisy::BLobToDetResult() roi.height = blobs.blobTab[i].maxy - blobs.blobTab[i].miny + 1; int config_qx_type = blobs.blobTab[i].AIclasstype; + if (config_qx_type < 0 || config_qx_type >= CONFIG_QX_NAME_count) + { + // AI 分类无对应缺陷类型(无效类别)时,不参与缺陷结果分析 + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "BLobToDetResult", + "blob idx %d invalid AIclasstype %d skip", i, config_qx_type); + continue; + } float JudgArea = blobs.blobTab[i].JudgArea; float fsecondArea = JudgArea;