diff --git a/AlgorithmModule/include/QX_Merge_Analysis.h b/AlgorithmModule/include/QX_Merge_Analysis.h index acd038a..722c4f1 100644 --- a/AlgorithmModule/include/QX_Merge_Analysis.h +++ b/AlgorithmModule/include/QX_Merge_Analysis.h @@ -214,9 +214,17 @@ private: int Judge_OK_Config(); int Judge_NG_Config(); - int Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx); + // pAllowChannels 非空时仅列表内通道参与分析(主通道未配置时的回退: 仅次通道); + // nullptr=不过滤;非空但为空列表=不做任何通道分析(直接返回) + int Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx, const std::vector *pAllowChannels = nullptr); int Analysis_AD_Bind(ALL_Qx_DataList *pALLTypeqxList, int qx_idx); + // 绑定分析的主通道是否未配置(2s 或 3s 的主通道列表为空); + // 只按配置判断,与本次产品是否检出缺陷无关(良品主通道本来就没暗点,按缺陷判断会频繁误回退) + bool IsADBindMainChannelConfigEmpty(); + // 绑定分析的次通道列表(second_channel_2s + second_channel_3s, 去重并过滤空名) + std::vector GetADBindSecondChannels(); + // 关联过滤源过滤:移除与关联通道过滤源缺陷重叠的缺陷,使其不参与后续数量/距离/S分析 int FilterCorrelatedDirtyQx(ALL_Qx_DataList *pALLTypeqxList); diff --git a/AlgorithmModule/src/QX_Merge_Analysis.cpp b/AlgorithmModule/src/QX_Merge_Analysis.cpp index 941fc0c..f6d534f 100644 --- a/AlgorithmModule/src/QX_Merge_Analysis.cpp +++ b/AlgorithmModule/src/QX_Merge_Analysis.cpp @@ -303,6 +303,29 @@ static bool IsValidCorrelatedName(const std::string &strName) return true; } +/** + * @brief 判断通道名是否在通道列表中(忽略大小写) + * 通道配置由人工填写(如 "Blue" 与实际通道名 "BLUE"),大小写敏感匹配会漏掉应参与的通道 + * @param channels 通道列表 + * @param channelName 待判断的通道名 + * @return bool 在列表中返回true + */ +static bool IsChannelInList(const std::vector &channels, const std::string &channelName) +{ + if (channelName.empty()) + { + return false; + } + for (int i = 0; i < (int)channels.size(); i++) + { + if (CheckUtil::compareIgnoreCase(channels.at(i), channelName)) + { + return true; + } + } + return false; +} + /** * @brief 判断缺陷类型是否为配置的关联过滤源类型(analysis_bind.correlated_types, * 如 "Dirty_L0"/"Dirty_L1"/"Dirty_L2"/"AD"), 登记与过滤两处共用此判定 @@ -712,7 +735,7 @@ int QX_Merge_Analysis::Analysis_single_Config(QXAnalysis_Config *pconfig, QX_cha if (qx_type == QX_ANALYSIS_AD && m_pbaseCheckFunction) { std::vector merge_analysy_region_sf = m_pbaseCheckFunction->ad_check.analysis_sf.region_sf; - if (std::find(merge_analysy_region_sf.begin(), merge_analysy_region_sf.end(), pqxList->channel_name) == merge_analysy_region_sf.end()) + if (!IsChannelInList(merge_analysy_region_sf, pqxList->channel_name)) { pchannelLog->AddCheckstr(PrintLevel_1, 3, "Analysis_single_Config", "merge_analysy_region_sf not found %s --> skip", pqxList->channel_name.c_str()); return 0; @@ -999,6 +1022,57 @@ int QX_Merge_Analysis::Analysis_single_Config(QXAnalysis_Config *pconfig, QX_cha return 0; } +/** + * @brief 绑定分析的主通道是否未配置(2s 或 3s 的主通道列表为空) + * 只按配置判断,与本次产品是否检出缺陷无关:良品主通道本来就没暗点, + * 若按“本次无匹配通道”判定回退,则每个良品都会回退,把次通道噪声直接判成 NG + * @return bool 需要回退用次通道走 Analysis_AD 老逻辑返回true + */ +bool QX_Merge_Analysis::IsADBindMainChannelConfigEmpty() +{ + if (m_pbaseCheckFunction == nullptr) + { + return false; + } + const AD_Analysisy_Bind &bind = m_pbaseCheckFunction->ad_check.analysis_bind; + // 2s/3s 任一主通道未配置,就无法完成对应的绑定判定,整体回退 + return bind.main_channel_2s.empty() || bind.main_channel_3s.empty(); +} + +/** + * @brief 取绑定分析的次通道列表(second_channel_2s + second_channel_3s) + * 去重忽略大小写,并过滤空名 + * @return std::vector 次通道名列表 + */ +std::vector QX_Merge_Analysis::GetADBindSecondChannels() +{ + std::vector channels; + if (m_pbaseCheckFunction == nullptr) + { + return channels; + } + const AD_Analysisy_Bind &bind = m_pbaseCheckFunction->ad_check.analysis_bind; + const std::vector *pListArr[2] = {&bind.second_channel_2s, &bind.second_channel_3s}; + for (int i = 0; i < 2; i++) + { + const std::vector &channelList = *pListArr[i]; + for (int j = 0; j < (int)channelList.size(); j++) + { + const std::string &channelName = channelList.at(j); + if (channelName.empty()) + { + continue; + } + // 2s/3s 重复配同一通道时只保留一份 + if (!IsChannelInList(channels, channelName)) + { + channels.push_back(channelName); + } + } + } + return channels; +} + int QX_Merge_Analysis::Analysis_QXtype(ALL_Qx_DataList *pALLTypeqxList, int qx_idx) { @@ -1021,17 +1095,41 @@ int QX_Merge_Analysis::Analysis_QXtype(ALL_Qx_DataList *pALLTypeqxList, int qx_i Analysis_Channel(pQXChannelList, qx_idx); } // AD 要进行额外的分析 - if (qx_idx == QX_ANALYSIS_AD) + if (qx_idx == QX_ANALYSIS_AD && m_pbaseCheckFunction != nullptr) { - if(m_pbaseCheckFunction->ad_check.analysis_bind.bUse) + if (m_pbaseCheckFunction->ad_check.analysis_bind.bUse) { - Analysis_AD_Bind(pALLTypeqxList, qx_idx); + if (IsADBindMainChannelConfigEmpty()) + { + // 主通道未配置(2s 或 3s 的主通道列表为空):无法做绑定判定, + // 回退用次通道列表走 Analysis_AD 老逻辑(产品级数量/距离/S 分析)。 + // 关联通道过滤(FilterCorrelatedDirtyQx)已在上面执行,回退路径同样受其保护。 + std::vector fallback_channels = GetADBindSecondChannels(); + std::string strFallbackChannels; + for (int k = 0; k < (int)fallback_channels.size(); k++) + { + if (k > 0) + { + strFallbackChannels += ","; + } + strFallbackChannels += fallback_channels.at(k); + } + m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, "Analysis_AD_Bind", + "main channel config empty(main_2s %d main_3s %d) --> fallback Analysis_AD (only second channels: %s)", + (int)m_pbaseCheckFunction->ad_check.analysis_bind.main_channel_2s.size(), + (int)m_pbaseCheckFunction->ad_check.analysis_bind.main_channel_3s.size(), + fallback_channels.empty() ? "" : strFallbackChannels.c_str()); + Analysis_AD(pALLTypeqxList, qx_idx, &fallback_channels); + } + else + { + Analysis_AD_Bind(pALLTypeqxList, qx_idx); + } } else { Analysis_AD(pALLTypeqxList, qx_idx); } - } return 0; @@ -1073,7 +1171,7 @@ int QX_Merge_Analysis::Judge_NG_Config() return 0; } -int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx) +int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx, const std::vector *pAllowChannels) { AD_list.clear(); if (!m_pbaseCheckFunction) @@ -1081,6 +1179,15 @@ int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx) return 1; } + // 回退模式(主通道未配置)且次通道也没配置:没有任何通道可参与,直接返回, + // 避免“空允许列表导致所有通道被 skip”的隐晦行为 + if (pAllowChannels != nullptr && pAllowChannels->empty()) + { + m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, "Analysis_AD", + "fallback allow list(second channels) is empty --> skip AD analysis"); + return 0; + } + Base_Function_AD_Check *pAd_checkParma = &m_pbaseCheckFunction->ad_check; int product_AD_num = 0; // 整个产品的 暗点数目。 @@ -1092,6 +1199,17 @@ int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx) { QX_channel_List *pQXChannelList = &pALLTypeqxList->channelqxList[i]; + // 回退模式:只允许次通道列表内的通道参与分析(忽略大小写) + if (pAllowChannels != nullptr && !IsChannelInList(*pAllowChannels, pQXChannelList->channel_name)) + { + if (pQXChannelList->pChannelDetlog) + { + pQXChannelList->pChannelDetlog->AddCheckstr(PrintLevel_1, 3, "Analysis_AD", + "channel %s not in fallback allow list(second channels) --> skip", + pQXChannelList->channel_name.c_str()); + } + continue; + } if (!pAd_checkParma) { continue; @@ -1336,6 +1454,17 @@ int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx) { QX_channel_List *pQXChannelList = &pALLTypeqxList->channelqxList[i]; + // 回退模式:只允许次通道列表内的通道被判定NG(忽略大小写) + if (pAllowChannels != nullptr && !IsChannelInList(*pAllowChannels, pQXChannelList->channel_name)) + { + if (pQXChannelList->pChannelDetlog) + { + pQXChannelList->pChannelDetlog->AddCheckstr(PrintLevel_1, 3, "Analysis_AD", + "channel %s not in fallback allow list(second channels) --> skip NG mark", + pQXChannelList->channel_name.c_str()); + } + continue; + } if (!pAd_checkParma->bOpen) { @@ -1531,13 +1660,15 @@ int QX_Merge_Analysis::Analysis_AD_Bind(ALL_Qx_DataList *pALLTypeqxList, int qx_ string cur_channel_name = pQXChannelList->channel_name; - if (std::find(main_channel_2s.begin(), main_channel_2s.end(), cur_channel_name) != main_channel_2s.end() || - std::find(main_channel_3s.begin(), main_channel_3s.end(), cur_channel_name) != main_channel_3s.end()) + // 通道名忽略大小写匹配:配置由人工填写(如 "Blue" 而实际通道是 "BLUE"), + // 大小写敏感匹配会让应参与的通道被静默排除 + if (IsChannelInList(main_channel_2s, cur_channel_name) || + IsChannelInList(main_channel_3s, cur_channel_name)) { main_channel_s_qxList.push_back(pALLTypeqxList->channelqxList[i]); } - if (std::find(second_channel_2s.begin(), second_channel_2s.end(), cur_channel_name) != second_channel_2s.end() || - std::find(second_channel_3s.begin(), second_channel_3s.end(), cur_channel_name) != second_channel_3s.end()) + if (IsChannelInList(second_channel_2s, cur_channel_name) || + IsChannelInList(second_channel_3s, cur_channel_name)) { second_channel_s_qxList.push_back(pALLTypeqxList->channelqxList[i]); } @@ -1640,13 +1771,13 @@ int QX_Merge_Analysis::Analysis_AD_Bind(ALL_Qx_DataList *pALLTypeqxList, int qx_ // roi_expand, fiou); if (fiou > pAd_checkParma->analysis_iou.AD_IOU) { - if (std::find(second_channel_2s.begin(), second_channel_2s.end(), second_channel_name) != second_channel_2s.end()) + if (IsChannelInList(second_channel_2s, second_channel_name)) { tmp_main_qxInfo.s_name_2.push_back(second_channel_name); m_pMergedetlog->AddCheckstr(PrintLevel_3, 3, "Add 2s", "%s roi1 %s roi2 %s expand %d iou %f", second_channel_name.c_str(), CheckUtil::GetRectString(roi_main).c_str(),CheckUtil::GetRectString(roi_second).c_str(),roi_expand, fiou); } - if(std::find(second_channel_3s.begin(), second_channel_3s.end(), second_channel_name) != second_channel_3s.end()) + if(IsChannelInList(second_channel_3s, second_channel_name)) { tmp_main_qxInfo.s_name_3.push_back(second_channel_name); m_pMergedetlog->AddCheckstr(PrintLevel_3, 3, "Add 3s", "%s roi1 %s roi2 %s expand %d iou %f", @@ -1724,7 +1855,7 @@ int QX_Merge_Analysis::Analysis_AD_Bind(ALL_Qx_DataList *pALLTypeqxList, int qx_ { continue; } - if(std::find(merge_analysy_s_sf.begin(), merge_analysy_s_sf.end(), pQXChannelList->channel_name) != merge_analysy_s_sf.end()) + if (IsChannelInList(merge_analysy_s_sf, pQXChannelList->channel_name)) { } else