Compare commits

..

No commits in common. 'bf853544ec561c73cbba65f349b74a3b33e337a6' and '4e95e93a91a85dc0ff11b9199234cd041a405f4b' have entirely different histories.

@ -599,8 +599,8 @@ int AIModel_Impl::F2softmaxId(float *data, int class_num, float *fmaxScore)
std::vector<float> output(cls_num);
for (int i = 0; i < output.size(); i++)
{
// float outi = (float)exp(data[i]) / (float)total;
output[i] = data[i]; // 不做softmax处理(模型已经做了)
float outi = (float)exp(data[i]) / (float)total;
output[i] = outi;
// output.push_back(outi);
// std::cout<<"output["<<i<<"]="<<output[i]<<std::endl;
}

@ -9,30 +9,21 @@
#ifndef AICommonDefine_H_
#define AICommonDefine_H_
// AI缺陷分类缺陷的种类(只含主检测+分类模型输出的类别;支架偏移/支架缺失/把手缺失为传统算法检出,
// 铜箔标签缺陷(TBBQ)同时可能由标签检测上报;顺序需与分类模型输出的 label 顺序一致)
// 判定规则:
// 1) DarkAoHen(暗场凹痕)只有暗场相机存在,明场相机分到该类时按 QiTa(其他)处理;
// 2) DarkAoHen 目前不做检测,分到该类按 OK 处理(不上报缺陷,仅记日志);
// 3) label 超出本表范围(AI_CLass_QX_NAME_count)时,按 TBMark(铜箔标记)处理。
// AI缺陷分类缺陷的种类(只含主检测+分类的缺陷;支架偏移/支架缺失/把手缺失为传统算法检出,铜箔标签(TBBQ)由标签检测上报,均不走AI分类)
enum AI_CLass_QX_NAME_
{
AI_CLass_QX_NAME_DarkAoHen, // 暗场凹痕(只有暗场检出)
AI_CLass_QX_NAME_TBMark, // 铜箔标记
AI_CLass_QX_NAME_QiPao, // 气泡
AI_CLass_QX_NAME_TBBQ, // 标签(铜箔面不良标签)
AI_CLass_QX_NAME_QiTa, // 其他
AI_CLass_QX_NAME_CGMark, // 把手标记
AI_CLass_QX_NAME_BQBMark, // 补强板标记
AI_CLass_QX_NAME_TBMark, // 铜箔标记
AI_CLass_QX_NAME_count,
};
// 缺陷项对应在参数中的名称(顺序需与分类模型输出的label顺序一致)
static const std::string AI_CLass_QX_NAME_Names[] =
{
"DarkAoHen",
"CGMark",
"BQBMark",
"TBMark",
"QiPao",
"TBBQ",
"QiTa",
};
#endif

@ -60,11 +60,10 @@ enum ERROR_TYPE_
ERROR_TYPE_ZJ_PianYi, // 1 支架偏移
ERROR_TYPE_ZJ_QueShi, // 2 支架缺失
ERROR_TYPE_CGMo_QueShi, // 3 把手缺失
ERROR_TYPE_DarkAoHen, // 4 暗场凹痕
ERROR_TYPE_QiPao, // 5 气泡
ERROR_TYPE_CGMark, // 4 把手标记
ERROR_TYPE_BQBMark, // 5 补强板标记
ERROR_TYPE_TBBQ, // 6 铜箔标签
ERROR_TYPE_TBMark, // 7 铜箔标记
ERROR_TYPE_QiTa, // 8 其他
ERROR_TYPE_COUNT,
};
extern std::vector<std::string> QX_Result_Names;

@ -429,15 +429,10 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
std::string resultType = "OK";
// 不受区域参数控制的缺陷类型:区域参数未配置/未开启时,仍按"检出即 NG"上报
// ZJ_PianYi/ZJ_QueShi/CGMo_QueShi:传统算法检出
// TBBQ(铜箔标签):标签检测(或AI分类)给出,本身已按 BQ_Detect 开关/检出结果判定,不再受区域参数控制
if (!Judge_Status && (config_qx_type == CONFIG_QX_NAME_cell_ZJ_PianYi ||
config_qx_type == CONFIG_QX_NAME_cell_ZJ_QueShi ||
config_qx_type == CONFIG_QX_NAME_cell_CGMo_QueShi ||
config_qx_type == CONFIG_QX_NAME_cell_TBBQ))
// PIN_PianYi, 不受区域参数控制;
if (!Judge_Status && (config_qx_type == CONFIG_QX_NAME_cell_ZJ_PianYi || config_qx_type == CONFIG_QX_NAME_cell_ZJ_QueShi || config_qx_type == CONFIG_QX_NAME_cell_CGMo_QueShi))
{
pQxLog->AddCheckstr(PrintLevel_3, "qx info", " %s: region param not used -> report NG by base judge", qx_name.c_str());
pQxLog->AddCheckstr(PrintLevel_3, "qx info", " ZJ_PianYi | ZJ_QueShi | CGMo_QueShi: region param not used -> report NG by base judge");
Judge_Status = true;
bNG_Status = true;
}

@ -20,7 +20,7 @@
// AI缺陷分类模型开关:当前暂无分类模型(Class.engine),分类流程代码先预留;
// 待分类模型部署后,将 AI_CLASS_MODEL_ENABLE 置 1,即可恢复 AI_QX_Class_Thread 中的AI分类流程
#define AI_CLASS_MODEL_ENABLE 1
#define AI_CLASS_MODEL_ENABLE 0
// 把手提取/判定参数(下面宏是“未配置 JSON 时”的默认值;现场可在参数 JSON 的 Handle_Detect.form.handle_param
// 下按注释标注的键名配置同名参数覆盖它们,按产品/现场情况调整,无需改代码重编译):
@ -107,13 +107,25 @@ static std::string ClsTrimStr(const std::string &str)
return str.substr(begin, end - begin + 1);
}
// 分类映射配置:分类label -> 缺陷名称
// 组装程序内默认缺陷名称列表(AI_CLass_QX_NAME_Names)
static std::vector<std::string> BuildDefaultAIQXNames()
{
std::vector<std::string> 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<std::string> vecNames; // 缺陷名称列表
std::map<int, std::string> mapLabelName; // 分类 label -> 缺陷名称
};
// 读取分类映射配置(每行:label索引:缺陷名称;# 后内容忽略)
// 读取分类映射配置(每行:label索引:缺陷名称;冒号后名称按行序构成缺陷名称列表;# 后内容忽略)
static ClsLabelConfig ReadClsLabelConfig(const std::string &strFilePath)
{
ClsLabelConfig config;
@ -144,12 +156,13 @@ static ClsLabelConfig ReadClsLabelConfig(const std::string &strFilePath)
}
continue;
}
// label索引:缺陷名称
// 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;
@ -162,16 +175,27 @@ static const ClsLabelConfig &GetClsLabelConfig()
return config;
}
// 获取缺陷名称列表:优先取配置文件的名称列表,读不到用程序内默认列表
static const std::vector<std::string> &GetAIQXNameList()
{
static const std::vector<std::string> vecDefaultNames = BuildDefaultAIQXNames();
const ClsLabelConfig &config = GetClsLabelConfig();
if (!config.vecNames.empty())
{
return config.vecNames;
}
return vecDefaultNames;
}
// 缺陷名称 -> AI_CLass 枚举(找不到返回 count 表示无效)
// 注意:按程序内 AI_CLass_QX_NAME_Names 的枚举顺序匹配,而不是映射文件的行序,
// 避免 BM_cls.txt 行数/顺序与枚举不一致时类别错位
static int ClsNameToAItype(const std::string &strName)
{
for (int i = 0; i < AI_CLass_QX_NAME_count; i++)
const std::vector<std::string> &vecNames = GetAIQXNameList();
for (size_t i = 0; i < vecNames.size(); i++)
{
if (strName == AI_CLass_QX_NAME_Names[i])
if (strName == vecNames[i])
{
return i;
return (int)i;
}
}
return AI_CLass_QX_NAME_count;
@ -197,31 +221,32 @@ static std::map<int, int> BuildLabelTypeMap()
return mapLabelType;
}
// 获取分类 label 对应的 AI 缺陷类型:优先配置文件(只读一次),读不到按 label 顺序兜底
// 规则:
// ① label 超出 [0, AI_CLass_QX_NAME_count)(含推理失败返回的负值)→ 视为 TBMark;
// ② DarkAoHen(暗场凹痕)只有暗场相机存在:明场相机分到该类时按 QiTa(其他)处理。
static int GetClsTypeByLabel(int nLabel, bool bIsDark)
// 获取分类 label 对应的 AI 缺陷类型:优先配置文件(只读一次),读不到用默认顺序
static int GetClsTypeByLabel(int nLabel)
{
// ① 超出程序的类别表(模型类别数与程序不一致)→ 统一按铜箔标记处理
if (nLabel < 0 || nLabel >= AI_CLass_QX_NAME_count)
{
return AI_CLass_QX_NAME_TBMark;
}
// 未被映射文件覆盖时,按程序内 AI_CLass_QX_NAME_Names 的顺序与 label 对应
int cls_num = nLabel;
static const std::map<int, int> mapLabelType = BuildLabelTypeMap();
std::map<int, int>::const_iterator it = mapLabelType.find(nLabel);
if (it != mapLabelType.end())
{
cls_num = it->second;
return it->second;
}
// ② 暗场凹痕只有暗场检出:明场分到该类时按"其他"处理
if (cls_num == AI_CLass_QX_NAME_DarkAoHen && !bIsDark)
// 映射文件未读到、或该 label 未在文件中配置时,按程序内默认顺序兜底(避免文件缺失/不全导致异常)
int cls_num = AI_CLass_QX_NAME_count;
switch (nLabel)
{
cls_num = AI_CLass_QX_NAME_QiTa;
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;
}
@ -3495,34 +3520,8 @@ int ImgCheckAnalysisy::AI_QX_Class_Thread()
pAIDet = AI_Factory->Class;
}
// 明/暗场判定:相机名含 "dark" 视为暗场(与 ALIGN_REUSE_*、暗场检测模型判定一致)
std::string strProductName, strCameraName;
GetProductCameraStr(strProductName, strCameraName);
const bool bIsDark = !ReplaceDarkWithBright(strCameraName).empty();
int deal_image_width = pAIDet ? pAIDet->input_0.width : 0;
int deal_image_height = pAIDet ? pAIDet->input_0.height : 0;
// 分类模型不可用(未加载/未初始化/输入尺寸异常):全部按 TBMark 兑底(与无分类模型时行为一致);
// 否则裁剪/推理会失败,且 blob 的 AIclasstype 保持默认值 0 会被当成“支架偏移”上报
if (!pAIDet || !pAIDet->m_bInitSuccess || deal_image_width <= 0 || deal_image_height <= 0)
{
int defaultqx = AIClassTypeToConfigType(AI_CLass_QX_NAME_TBMark);
for (int i = 0; i < blobs.blobCount; i++)
{
blobs.blobTab[i].AIclasstype = defaultqx;
}
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, "QX_Class",
" class model not ready(m_bInitSuccess %d, input [w %d h %d]), all %d qx -> TBMark(%d)",
pAIDet ? (int)pAIDet->m_bInitSuccess : 0, deal_image_width, deal_image_height, blobs.blobCount, defaultqx);
return 0;
}
// 先统一置为无效类型:分类未提交/失败(裁剪失败、label 非法等)的 blob 不会落到某个真实缺陷类型上
for (int i = 0; i < blobs.blobCount; i++)
{
blobs.blobTab[i].AIclasstype = CONFIG_QX_NAME_count;
}
int deal_image_width = pAIDet->input_0.width;
int deal_image_height = pAIDet->input_0.height;
const int totalTasks = blobs.blobCount;
int submitted = 0;
@ -3539,8 +3538,8 @@ int ImgCheckAnalysisy::AI_QX_Class_Thread()
ERROR_DOTS_BLOB_DATA *pblob = &blobs.blobTab[submitted];
if (pblob->ErrType == ERR_TYPE_2)
{
// ERR_TYPE_2 不参与分类,按默认类别(铜箔标记)处理
pblob->AIclasstype = AIClassTypeToConfigType(AI_CLass_QX_NAME_TBMark);
// ERR_TYPE_2不参与分类,按默认AI类别(把手标记)处理
pblob->AIclasstype = CONFIG_QX_NAME_cell_CGMark;
submitted++;
completed++;
continue;
@ -3568,9 +3567,6 @@ int ImgCheckAnalysisy::AI_QX_Class_Thread()
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "QX_Class",
"blob idx %d crop class img failed(re %d) roi[%d %d %d %d] -> skip",
submitted, re, roi.x, roi.y, roi.width, roi.height);
completed++;
}
@ -3586,24 +3582,8 @@ int ImgCheckAnalysisy::AI_QX_Class_Thread()
int temClass = result->cls_label;
// label 越界(含推理失败返回的负值):按规则统一视为 TBMark
if (temClass < 0 || temClass >= AI_CLass_QX_NAME_count)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "QX_Class",
"blob idx %d invalid class label %d (valid 0~%d), use TBMark",
result->id, temClass, AI_CLass_QX_NAME_count - 1);
}
// 分类 label 映射:优先读映射配置文件(BM_cls.txt),读不到按 label 顺序;
// 明场分到暗场凹痕(DarkAoHen)时按 QiTa 处理
int cls_num = GetClsTypeByLabel(temClass, bIsDark);
// 明细日志:原始 label -> 最终类别(仅 -s / 详细日志级别输出)
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "QX_Class",
"blob idx %d label %d -> %s score %.3f (dark %d)",
result->id, temClass,
(cls_num >= 0 && cls_num < AI_CLass_QX_NAME_count) ? AI_CLass_QX_NAME_Names[cls_num].c_str() : "invalid",
result->cls_score, (int)bIsDark);
// 分类 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);
@ -3876,25 +3856,21 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
int ImgCheckAnalysisy::AIClassTypeToConfigType(int nAIQXType)
{
// AI 分类类别 -> 缺陷类型;支架偏移/支架缺失/把手缺失为传统算法检出,不在本表内
// 未知类别(含 AI_CLass_QX_NAME_count 及越界值)统一按铜箔标记处理
int resultError_type = CONFIG_QX_NAME_cell_TBMark;
// 只有主检测+分类的缺陷在映射表中;支架偏移/支架缺失/把手缺失(传统检出)、铜箔标签(标签检测)不走AI分类
int resultError_type = CONFIG_QX_NAME_cell_CGMark;
switch (nAIQXType)
{
case AI_CLass_QX_NAME_DarkAoHen:
resultError_type = CONFIG_QX_NAME_cell_DarkAoHen;
case AI_CLass_QX_NAME_CGMark:
resultError_type = CONFIG_QX_NAME_cell_CGMark;
break;
case AI_CLass_QX_NAME_BQBMark:
resultError_type = CONFIG_QX_NAME_cell_BQBMark;
break;
case AI_CLass_QX_NAME_TBMark:
resultError_type = CONFIG_QX_NAME_cell_TBMark;
break;
case AI_CLass_QX_NAME_QiPao:
resultError_type = CONFIG_QX_NAME_cell_QiPao;
break;
case AI_CLass_QX_NAME_TBBQ:
resultError_type = CONFIG_QX_NAME_cell_TBBQ;
break;
case AI_CLass_QX_NAME_QiTa:
resultError_type = CONFIG_QX_NAME_cell_QiTa;
case AI_CLass_QX_NAME_count: // 无效类别(分类映射配置中不存在):不参与缺陷分析
resultError_type = CONFIG_QX_NAME_count;
break;
default:
break;
@ -3934,20 +3910,12 @@ int ImgCheckAnalysisy::BLobToDetResult()
int config_qx_type = blobs.blobTab[i].AIclasstype;
if (config_qx_type < 0 || config_qx_type >= CONFIG_QX_NAME_count)
{
// AI 分类无对应缺陷类型(无效类别/分类失败)时,不参与缺陷结果分析
// AI 分类无对应缺陷类型(无效类别)时,不参与缺陷结果分析
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "BLobToDetResult",
"blob idx %d invalid AIclasstype %d skip", i, config_qx_type);
continue;
}
// 暗场凹痕目前不做检测:分到该类的 blob 直接按 OK 处理(不产生缺陷条目,仅记日志)
if (config_qx_type == CONFIG_QX_NAME_cell_DarkAoHen)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "BLobToDetResult",
"blob idx %d AIclasstype DarkAoHen -> OK (not detected yet), skip", i);
continue;
}
float JudgArea = blobs.blobTab[i].JudgArea;
float fsecondArea = JudgArea;
std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type];

@ -36,31 +36,28 @@ static const std::string WHITE_BLCAK_Names[] =
{
"black",
"white"};
// 检测缺陷的种类(前 7 项与 defect_list.json 前 7 条一一对应,勿随意调整顺序;
// 新增类型统一追加在末尾,结果名称/代码按名称对齐,不依赖下标)
// 检测缺陷的种类
enum CONFIG_QX_NAME_
{
CONFIG_QX_NAME_cell_ZJ_PianYi, // 分类 支架偏移
CONFIG_QX_NAME_cell_ZJ_QueShi, // 分类 支架缺失
CONFIG_QX_NAME_cell_CGMo_QueShi, // 分类 把手缺失
CONFIG_QX_NAME_cell_DarkAoHen, // 分类 暗场凹痕(AI分类,仅暗场;目前不检测)
CONFIG_QX_NAME_cell_QiPao, // 分类 气泡(AI分类)
CONFIG_QX_NAME_cell_TBBQ, // 分类 铜箔标签(标签检测 / AI分类)
CONFIG_QX_NAME_cell_TBMark, // 分类 铜箔标记(AI分类)
CONFIG_QX_NAME_cell_QiTa, // 分类 其他(AI分类)
CONFIG_QX_NAME_cell_CGMark, // 分类 把手标记
CONFIG_QX_NAME_cell_BQBMark, // 分类 补强板标记
CONFIG_QX_NAME_cell_TBBQ, // 分类 铜箔标签
CONFIG_QX_NAME_cell_TBMark, // 分类 铜箔标记
CONFIG_QX_NAME_count,
};
// 缺陷项对应在参数中的名称(Check_Param 的表单键名;顺序与上面对应)
// 缺陷项对应在参数中的名称
static std::vector<std::string> CONFIG_QX_NAME_Names =
{
"ZJ_PianYi",
"ZJ_QueShi",
"CGMo_QueShi",
"DarkAoHen",
"QiPao",
"CGMark",
"BQBMark",
"TBBQ",
"TBMark",
"QiTa",
};
// 分析类型

@ -10,7 +10,6 @@
#include "ConfigManager.h"
#include <regex>
#include "ConfigBase.h"
#include "CheckConfigDefine.h"
#include "../../AlgorithmModule/include/ImgCheckConfig.h"
ConfigManager::ConfigManager()
{
@ -23,29 +22,25 @@ ConfigManager::~ConfigManager()
std::vector<ReadFlawCode> m_FlawCodeList;
// 缺陷结果名称/代码默认值(下标与 CONFIG_QX_NAME_ 一一对应;使用英文缺陷名,与 CONFIG_QX_NAME_Names 一致;
// defect_list.json 中 desc 与 CONFIG_QX_NAME_Names 同名的条目会按名称覆盖这里的值)
std::vector<std::string> QX_Result_Names =
{
"ZJ_PianYi",
"ZJ_QueShi",
"CGMo_QueShi",
"DarkAoHen",
"QiPao",
"CGMark",
"BQBMark",
"TBBQ",
"TBMark",
"QiTa",
};
};
std::vector<std::string> QX_Result_Code =
{
"M701201",
"M200214",
"M200215",
"M200230", // TODO 暗场凹痕:缺陷代码待平台确认
"M200231", // TODO 气泡:缺陷代码待平台确认
"M200216",
"M200217",
"M200218",
"M200219",
"M200232", // TODO 其他:缺陷代码待平台确认
};
int ReadFlawCodeConfig(std::string json_path)
@ -90,36 +85,18 @@ int ReadFlawCodeConfig(std::string json_path)
}
m_FlawCodeList.push_back(tem);
}
// 结果名称/代码表按“缺陷名称”与程序内缺陷类型(CONFIG_QX_NAME_Names)对齐:
// defect_list.json 的 desc 字段(可用';'分隔多个名称)与 CONFIG_QX_NAME_Names 同名时覆盖名称/代码。
// 不再按下标覆盖:缺陷列表新增/减少/换序时不会把 A 类型的名称/代码写到 B 类型上。
while (QX_Result_Names.size() < (size_t)CONFIG_QX_NAME_count)
{
QX_Result_Names.push_back("");
}
while (QX_Result_Code.size() < (size_t)CONFIG_QX_NAME_count)
for(int i = 0; i < m_FlawCodeList.size(); i++)
{
QX_Result_Code.push_back("");
}
for (size_t i = 0; i < m_FlawCodeList.size(); i++)
{
for (int iqx = 0; iqx < CONFIG_QX_NAME_count; iqx++)
if(i >= QX_Result_Names.size()) {
QX_Result_Names.push_back(m_FlawCodeList.at(i).flaw_name);
QX_Result_Code.push_back(m_FlawCodeList.at(i).flaw_code);
}
else
{
bool bMatch = false;
for (size_t k = 0; k < m_FlawCodeList.at(i).config_flaw_name.size(); k++)
{
if (m_FlawCodeList.at(i).config_flaw_name.at(k) == CONFIG_QX_NAME_Names.at(iqx))
{
bMatch = true;
break;
}
}
if (bMatch)
{
QX_Result_Names.at(iqx) = m_FlawCodeList.at(i).flaw_name;
QX_Result_Code.at(iqx) = m_FlawCodeList.at(i).flaw_code;
}
QX_Result_Names.at(i) = m_FlawCodeList.at(i).flaw_name;
QX_Result_Code.at(i) = m_FlawCodeList.at(i).flaw_code;
}
}
return 0;
}

Loading…
Cancel
Save