From 3741c2282db7c39464ba9c2fd1e68d59025d68c4 Mon Sep 17 00:00:00 2001 From: liusiyang Date: Tue, 27 Oct 2026 17:19:57 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E5=AE=8C=E5=96=84=E7=9B=B2=E5=A4=B1?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AlgorithmModule/include/ImageResultJudge.h | 14 + AlgorithmModule/src/CameraCheckAnalysisy.cpp | 10 + AlgorithmModule/src/ImageResultJudge.cpp | 291 ++++++++++++++++--- ConfigModule/include/CheckConfigDefine.h | 2 +- 4 files changed, 279 insertions(+), 38 deletions(-) diff --git a/AlgorithmModule/include/ImageResultJudge.h b/AlgorithmModule/include/ImageResultJudge.h index 27309ad..9cca6c6 100644 --- a/AlgorithmModule/include/ImageResultJudge.h +++ b/AlgorithmModule/include/ImageResultJudge.h @@ -107,6 +107,10 @@ public: int UpdateConfig(std::string strcameraName); int DrawResult(std::shared_ptr pImageResult); + // 盲失联合判断:主通道(ms_main_channel)检出的盲失(Other)若在过滤通道(ms_filter_channels)同位置存在缺陷, + // 则不算盲失(撤销NG)。同位置判断参考暗点联合分析:双方按 ms_expand 扩张后计算 IOU, 大于 ms_iou 认为同位置 + int FilterMsBlindNG(std::shared_ptr pCheck_Result); + private: // 获取 缺陷在参数列表的位置 int GetParamidx(); @@ -135,6 +139,16 @@ private: int AnalysisResult_Pre(QX_ERROR_INFO_ *QX_info); // 弱化处理 + // 撤销单个缺陷的NG结果:恢复为OK,移除对应NG小图并回退计数 + int UndoNGResult(std::shared_ptr pImageResult, int qxidx, QX_ERROR_INFO_ *QX_info); + + // 判断盲失(产品坐标系ROI)在配置的过滤通道是否存在同位置缺陷(双方按 expand 扩张后计算IOU) + bool IsOverlapQxInMsFilterChannel(const std::vector> &imgResults, + const std::string &cur_channel_name, + const cv::Rect &qx_product_roi, + int roi_expand, float iou_threshold, + std::shared_ptr plog); + // 关联过滤源过滤:撤销与关联通道过滤源缺陷重叠的暗点NG结果(含单图判定的单点NG) int FilterCorrelatedDirtyNG(std::shared_ptr pImageResult); diff --git a/AlgorithmModule/src/CameraCheckAnalysisy.cpp b/AlgorithmModule/src/CameraCheckAnalysisy.cpp index dcedcfd..dd46f6a 100644 --- a/AlgorithmModule/src/CameraCheckAnalysisy.cpp +++ b/AlgorithmModule/src/CameraCheckAnalysisy.cpp @@ -341,6 +341,16 @@ int CameraCheckAnalysisy::ResultParamJudge() int re = m_imageResultJudge.ResultJudge(m_pCheck_Result->ImageALLDetResultList.at(imgidx)); long t2 = CheckUtil::getcurTime(); } + + // 盲失联合判断:主通道(ms_main_channel)检出的盲失若在过滤通道(ms_filter_channels)同位置存在盲失,则不算盲失(撤销NG) + { + int nMsFilterNum = m_imageResultJudge.FilterMsBlindNG(m_pCheck_Result); + if (nMsFilterNum > 0) + { + m_pdetlog->AddCheckstr(PrintLevel_1, "ResultParamJudge", "盲失联合判断: 过滤通道同位置存在盲失, 过滤盲失NG %d 个", nMsFilterNum); + } + } + m_pQX_Merge_Analysis->setSendStatus(m_strcameraName, QX_Merge_Analysis::RUN_STATUS_READY); m_pdetlog->AddCheckstr(PrintLevel_1, "ResultParamJudge", "===============QX_Merge_Analysis===start %s", CheckUtil::getCurTimeHMS().c_str()); diff --git a/AlgorithmModule/src/ImageResultJudge.cpp b/AlgorithmModule/src/ImageResultJudge.cpp index 6e80455..28b3c08 100644 --- a/AlgorithmModule/src/ImageResultJudge.cpp +++ b/AlgorithmModule/src/ImageResultJudge.cpp @@ -1,6 +1,7 @@ #include "ImageResultJudge.h" #include +#include ImageResultJudge::ImageResultJudge(/* args */) { @@ -869,59 +870,275 @@ int ImageResultJudge::FilterCorrelatedDirtyNG(std::shared_ptr pI } // 撤销NG结果 - QX_info->result = QX_RESULT_TYPE_OK; - QX_info->result_name = QX_RESULT_TYPE_Names[QX_info->result]; - int nqx_type = ConfigTypeToResultType(QX_info->nconfig_qx_type); + UndoNGResult(pImageResult, qxidx, QX_info); + + nFilterNum++; + if (pImageLog) + { + pImageLog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "DirtyCorrelatedFilter", + "img %s cam %s blob %d qx %s roi %s : 关联通道存在重叠的过滤源缺陷, 过滤该暗点NG", + pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(), + qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str()); + } + if (m_pQX_Merge_Analysis->m_pMergedetlog) + { + m_pQX_Merge_Analysis->m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, "DirtyCorrelatedFilter", + "img %s cam %s blob %d qx %s roi %s : 关联通道存在重叠的过滤源缺陷, 过滤该暗点NG", + pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(), + qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str()); + } + } + + return nFilterNum; +} + +/** + * @brief 撤销单个缺陷的NG结果:恢复为OK,移除对应的NG小图并回退计数 + * @param pImageResult 当前检测图片结果 + * @param qxidx 缺陷在 pQx_ErrorList 中的下标 + * @param QX_info 缺陷信息 + * @return int 移除的NG小图个数 + */ +int ImageResultJudge::UndoNGResult(std::shared_ptr pImageResult, int qxidx, QX_ERROR_INFO_ *QX_info) +{ + if (pImageResult == nullptr || QX_info == nullptr) + { + return 0; + } + std::shared_ptr m_CheckResult_shareP = pImageResult->result; + if (m_CheckResult_shareP == nullptr) + { + return 0; + } + + // 撤销NG结果 + QX_info->result = QX_RESULT_TYPE_OK; + QX_info->result_name = QX_RESULT_TYPE_Names[QX_info->result]; + int nqx_type = ConfigTypeToResultType(QX_info->nconfig_qx_type); + + // 移除该缺陷的NG小图 + int nEraseNum = 0; + for (auto it = m_CheckResult_shareP->qxImageResult.begin(); it != m_CheckResult_shareP->qxImageResult.end();) + { + if (it->idx == qxidx) + { + it = m_CheckResult_shareP->qxImageResult.erase(it); + nEraseNum++; + } + else + { + ++it; + } + } + + // 计数回退 + if (nqx_type >= 0 && nqx_type < ERROR_TYPE_COUNT) + { + m_CheckResult_shareP->defectResultList[nqx_type].num -= nEraseNum; + if (m_CheckResult_shareP->defectResultList[nqx_type].num < 0) + { + m_CheckResult_shareP->defectResultList[nqx_type].num = 0; + } + } + if (pImageResult->NG_num > 0) + { + pImageResult->NG_num--; + } + pImageResult->Ok_num++; + // 过滤后已无NG,则恢复图片结果 + if (m_CheckResult_shareP->qxImageResult.empty()) + { + m_CheckResult_shareP->nresult = ERROR_TYPE_OK; + } + + return nEraseNum; +} - // 移除该缺陷的NG小图 - int nEraseNum = 0; - for (auto it = m_CheckResult_shareP->qxImageResult.begin(); it != m_CheckResult_shareP->qxImageResult.end();) +/** + * @brief 判断盲失(产品坐标系ROI)在配置的过滤通道是否存在同位置且同为盲失(Other)类型的缺陷 + * 同位置判断方式参考暗点联合分析:当前ROI与过滤通道缺陷ROI双方均按 expand 扩张后计算 IOU + * @param imgResults 当前相机所有通道的检测结果 + * @param cur_channel_name 盲失所属通道(主通道) + * @param qx_product_roi 盲失的产品坐标系ROI + * @param roi_expand 扩张像素(ms_expand) + * @param iou_threshold IOU阈值(ms_iou) + * @param plog 日志 + * @return bool 过滤通道同位置存在盲失缺陷返回true + */ +bool ImageResultJudge::IsOverlapQxInMsFilterChannel(const std::vector> &imgResults, + const std::string &cur_channel_name, + const cv::Rect &qx_product_roi, + int roi_expand, float iou_threshold, + std::shared_ptr plog) +{ + // 防御:ROI非法时不判断,避免无效计算与除零 + if (qx_product_roi.width <= 0 || qx_product_roi.height <= 0) + { + return false; + } + if (m_pbaseCheckFunction == nullptr || m_pAnalysisyConfig == nullptr) + { + return false; + } + const std::vector &filter_channels = m_pbaseCheckFunction->ms_check.ms_filter_channels; + if (filter_channels.empty()) + { + return false; + } + float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm; + int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X); + + // 与暗点联合分析保持一致:双方均按 expand 扩张后再计算 IOU + cv::Rect roi_expanded(qx_product_roi.x - roi_expand, qx_product_roi.y - roi_expand, + qx_product_roi.width + roi_expand * 2, qx_product_roi.height + roi_expand * 2); + + for (int imgidx = 0; imgidx < (int)imgResults.size(); imgidx++) + { + std::shared_ptr pFilterImage = imgResults.at(imgidx); + if (pFilterImage == nullptr) { - if (it->idx == qxidx) + continue; + } + // 只与配置的过滤通道比较;当前通道不参与,避免自身重叠导致误过滤 + if (pFilterImage->strChannel == cur_channel_name) + { + continue; + } + if (std::find(filter_channels.begin(), filter_channels.end(), pFilterImage->strChannel) == filter_channels.end()) + { + continue; + } + std::shared_ptr pFilterDetResult = pFilterImage->pDetResult; + if (pFilterDetResult == nullptr || pFilterDetResult->pQx_ErrorList == nullptr) + { + continue; + } + + // 只统计过滤通道内同为盲失(Other)类型的缺陷,与具体判定结果无关 + for (int j = 0; j < (int)pFilterDetResult->pQx_ErrorList->size(); j++) + { + const QX_ERROR_INFO_ &filter_qx = pFilterDetResult->pQx_ErrorList->at(j); + if (filter_qx.nconfig_qx_type != CONFIG_QX_NAME_Other) { - it = m_CheckResult_shareP->qxImageResult.erase(it); - nEraseNum++; + continue; } - else + cv::Rect filter_roi = filter_qx.roi; + filter_roi.x += fproduct_offx_piexl; + cv::Rect filter_expanded(filter_roi.x - roi_expand, filter_roi.y - roi_expand, + filter_roi.width + roi_expand * 2, filter_roi.height + roi_expand * 2); + + float fiou = CheckUtil::CalIoU(roi_expanded, filter_expanded); + if (plog) { - ++it; + plog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "MsBlind", + "main channel %s roi %s <-> filter channel %s qx %d roi %s expand %d iou %f", + cur_channel_name.c_str(), CheckUtil::GetRectString(qx_product_roi).c_str(), + pFilterImage->strChannel.c_str(), j, + CheckUtil::GetRectString(filter_roi).c_str(), roi_expand, fiou); } - } - - // 计数回退 - if (nqx_type >= 0 && nqx_type < ERROR_TYPE_COUNT) - { - m_CheckResult_shareP->defectResultList[nqx_type].num -= nEraseNum; - if (m_CheckResult_shareP->defectResultList[nqx_type].num < 0) + // IOU 为 NaN(ROI 非法导致的除零)时比较结果为 false,不会误过滤 + if (fiou > iou_threshold) { - m_CheckResult_shareP->defectResultList[nqx_type].num = 0; + if (plog) + { + plog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "MsBlind", + "main channel %s roi %s overlap filter channel %s qx %d roi %s expand %d iou %f > %f", + cur_channel_name.c_str(), CheckUtil::GetRectString(qx_product_roi).c_str(), + pFilterImage->strChannel.c_str(), j, + CheckUtil::GetRectString(filter_roi).c_str(), roi_expand, fiou, iou_threshold); + } + return true; } } - if (pImageResult->NG_num > 0) + } + + return false; +} + +/** + * @brief 盲失联合判断:主通道(ms_main_channel)检出的盲失(Other)需要和过滤通道(ms_filter_channels)同位置判断, + * 过滤通道同位置存在同为盲失(Other)类型的缺陷时不算盲失(撤销NG) + * 同位置判断方式参考暗点联合分析:双方按 ms_expand 扩张后计算 IOU, 大于 ms_iou 认为同位置 + * @param pCheck_Result 当前相机检测结果(包含该相机所有通道的检测结果) + * @return int 被过滤的盲失NG个数 + */ +int ImageResultJudge::FilterMsBlindNG(std::shared_ptr pCheck_Result) +{ + if (pCheck_Result == nullptr || m_pbaseCheckFunction == nullptr || m_pAnalysisyConfig == nullptr) + { + return 0; + } + Base_Function_MS_Check *pMsParam = &m_pbaseCheckFunction->ms_check; + // 未开启盲失分析 或 未配置主通道/过滤通道 时不需要判断 + if (!pMsParam->bOpen || pMsParam->ms_main_channel.empty() || pMsParam->ms_filter_channels.empty()) + { + return 0; + } + + const int roi_expand = (int)(round)(pMsParam->MS_Expand); + const float iou_threshold = pMsParam->MS_IOU; + float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm; + int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X); + + int nFilterNum = 0; + for (int imgidx = 0; imgidx < (int)pCheck_Result->ImageALLDetResultList.size(); imgidx++) + { + std::shared_ptr pImageResult = pCheck_Result->ImageALLDetResultList.at(imgidx); + if (pImageResult == nullptr) { - pImageResult->NG_num--; + continue; } - pImageResult->Ok_num++; - // 过滤后已无NG,则恢复图片结果 - if (m_CheckResult_shareP->qxImageResult.empty()) + // 只处理配置的主通道 + if (std::find(pMsParam->ms_main_channel.begin(), pMsParam->ms_main_channel.end(), pImageResult->strChannel) == pMsParam->ms_main_channel.end()) { - m_CheckResult_shareP->nresult = ERROR_TYPE_OK; + continue; } - - nFilterNum++; - if (pImageLog) + std::shared_ptr pDetResult = pImageResult->pDetResult; // 检测结果 + if (pDetResult == nullptr || pDetResult->pQx_ErrorList == nullptr || pImageResult->result == nullptr) { - pImageLog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "DirtyCorrelatedFilter", - "img %s cam %s blob %d qx %s roi %s : 关联通道存在重叠的过滤源缺陷, 过滤该暗点NG", - pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(), - qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str()); + continue; } - if (m_pQX_Merge_Analysis->m_pMergedetlog) + std::shared_ptr pImageLog = pImageResult->detlog; + + for (int qxidx = 0; qxidx < (int)pDetResult->pQx_ErrorList->size(); qxidx++) { - m_pQX_Merge_Analysis->m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, "DirtyCorrelatedFilter", - "img %s cam %s blob %d qx %s roi %s : 关联通道存在重叠的过滤源缺陷, 过滤该暗点NG", - pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(), - qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str()); + QX_ERROR_INFO_ *QX_info = &pDetResult->pQx_ErrorList->at(qxidx); + // 只处理 盲失(Other) 的 NG 结果 + if (QX_info->nconfig_qx_type != CONFIG_QX_NAME_Other || QX_info->result != QX_RESULT_TYPE_NG) + { + continue; + } + + cv::Rect product_roi = QX_info->roi; + product_roi.x += fproduct_offx_piexl; + // 过滤通道同位置不存在盲失时,保留盲失NG + if (!IsOverlapQxInMsFilterChannel(pCheck_Result->ImageALLDetResultList, + pImageResult->strChannel, + product_roi, + roi_expand, iou_threshold, + pImageLog)) + { + continue; + } + + // 撤销NG结果 + UndoNGResult(pImageResult, qxidx, QX_info); + nFilterNum++; + + if (pImageLog) + { + pImageLog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "MsBlind", + "img %s cam %s blob %d qx %s roi %s : 过滤通道同位置存在盲失, 不算盲失", + pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(), + qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str()); + } + if (m_pQX_Merge_Analysis != nullptr && m_pQX_Merge_Analysis->m_pMergedetlog) + { + m_pQX_Merge_Analysis->m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, "MsBlind", + "img %s cam %s blob %d qx %s roi %s : 过滤通道同位置存在盲失, 不算盲失", + pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(), + qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str()); + } } } diff --git a/ConfigModule/include/CheckConfigDefine.h b/ConfigModule/include/CheckConfigDefine.h index a30f782..f1afd54 100644 --- a/ConfigModule/include/CheckConfigDefine.h +++ b/ConfigModule/include/CheckConfigDefine.h @@ -128,7 +128,7 @@ enum Function_CONFIG_QX_NAME_ Function_CONFIG_QX_NAME_PS, // ps(P0002) Function_CONFIG_QX_NAME_Weak_Bright_Mura, // 白GAP(P1654) Function_CONFIG_QX_NAME_No_Label, // 缺POL(P2833) - Function_CONFIG_QX_NAME_Other, // 缺POL(P2833) + Function_CONFIG_QX_NAME_Other, // 盲失 Function_CONFIG_QX_NAME_Chess, // Chess 异常 Function_CONFIG_QX_NAME_127Cell, // 白cell Function_CONFIG_QX_NAME_Cell, // 独立检测的黑白cell点