From b7b8c100ef33502a5799ee0f30d9d91d863a17af Mon Sep 17 00:00:00 2001 From: liusiyang Date: Fri, 9 Oct 2026 11:28:17 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E6=B8=85=E7=90=86=E6=8A=8A=E6=89=8B?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E5=A4=9A=E4=BD=99=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AlgorithmModule/include/ImgCheckAnalysisy.hpp | 10 +- AlgorithmModule/src/ImgCheckAnalysisy.cpp | 103 +++--------------- ConfigModule/include/CheckConfigDefine.h | 73 +++---------- ConfigModule/src/JsonConfig.cpp | 78 +++++-------- 4 files changed, 58 insertions(+), 206 deletions(-) diff --git a/AlgorithmModule/include/ImgCheckAnalysisy.hpp b/AlgorithmModule/include/ImgCheckAnalysisy.hpp index 8d79dbf..4b81630 100644 --- a/AlgorithmModule/include/ImgCheckAnalysisy.hpp +++ b/AlgorithmModule/include/ImgCheckAnalysisy.hpp @@ -50,18 +50,16 @@ enum AT_THRESHOLD_TYPE_ // 全局静态变量, 记录图像灰度值异常累计数量 static int g_nImgBrightnessErrorCount = 0; -// 把手候选:bigmask 凸起 + 实测特征(后续增加面积/灰阶等判定时直接用这些实测值) +// 把手候选:bigmask 凸起 + 实测特征(面积/填充率,仅供日志排查) struct Handle_Candidate_ { Mask_Protrusion_ protr; // 凸起几何信息(roi / 沿边长度 / 深度 / 面积 / 填充率) int nLen; // 沿边长度(像素) int nDepth; // 垂直深度(像素) - float fGrayDiff; // 凸起区域与周边背景的平均灰度差(预留灰阶判定) Handle_Candidate_() { nLen = 0; nDepth = 0; - fGrayDiff = 0.0f; } }; @@ -158,15 +156,13 @@ private: // 发布本图定位结果/失败标记(供同产品其它相机复用) void PublishAlignShare(bool bSucc); // 用 bigmask 检测把手(bigmask 主体近似矩形,把手是四周凸起的矩形块) - int CheckHandleByBigMask(const cv::Mat &img, const cv::Mat &bigmask); + int CheckHandleByBigMask(const cv::Mat &bigmask); // 标签检测(独立步骤):用 (图像 + bigmask) 跑 Tag_Loc 模型,检出结果存 m_tag_roiList // 明场传自身 bigmask;暗场不做定位模型,传复用明场的 bigmask int CheckTagByBigMask(const cv::Mat &img, const cv::Mat &bigmask); // 在某个把手区域附近找宽高匹配的凸起:返回凸起在 candList 中的下标(-1=没找到) int MatchHandleRegion(const std::vector &candList, const std::vector &usedList, - const cv::Rect &boxRegion, const Handle_Check_Param ¶m, cv::Rect &roiHandle); - // 计算凸起区域与周边背景的平均灰度差(灰阶判定的预留量测值,img 需为单通道) - float CalProtrusionGrayDiff(const cv::Mat &grayImg, const cv::Rect &roi); + const cv::Rect &boxRegion, cv::Rect &roiHandle); // 计算产品尺寸 int CalProductSize(); // 图片预处理 diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index fbb7e47..238596b 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -21,7 +21,7 @@ #define HANDLE_SIZE_MAX_RATIO 1.2f // "附近"的搜索范围:把手区域外接矩形在宽/高方向各外扩该比例 #define HANDLE_NEAR_EXPAND_RATIO 5 -// 凸起填充率下限等参数的默认值在 Handle_Check_Param(CheckConfigDefine.h)里 +// 把手缺失检测开关见 Function_Support_Det::bDetHandle(CheckConfigDefine.h) // 用于排序轮廓的比较函数 static bool compareContourAreas(const vector &contour1, const vector &contour2) @@ -811,7 +811,7 @@ int ImgCheckAnalysisy::CheckRun() // 定位模型不跑,但把手检测仍要执行:暗场图像 + 明场 bigmask { long handle_s = CheckUtil::getcurTime(); - CheckHandleByBigMask(m_CheckResult_shareP->in_shareImage->img, m_pEdge_Align_Result->bigmask); + CheckHandleByBigMask(m_pEdge_Align_Result->bigmask); long handle_e = CheckUtil::getcurTime(); m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "handle detect use time %ld", handle_e - handle_s); } @@ -2552,7 +2552,7 @@ int ImgCheckAnalysisy::AI_Edge(const cv::Mat &img, cv::RotatedRect &outerRoi, cv /*使用m_pEdge_Align_Result->bigmask检测把手是否缺失*/ { long handle_s = CheckUtil::getcurTime(); - CheckHandleByBigMask(img, m_pEdge_Align_Result->bigmask); + CheckHandleByBigMask(m_pEdge_Align_Result->bigmask); long handle_e = CheckUtil::getcurTime(); m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "handle detect use time %ld", handle_e - handle_s); } @@ -2565,7 +2565,7 @@ int ImgCheckAnalysisy::AI_Edge(const cv::Mat &img, cv::RotatedRect &outerRoi, cv // 3、参数里每个非空的"把手区域"附近都应该有一个把手:在区域附近找凸起, // 凸起宽高与区域外接矩形宽高相近(HANDLE_SIZE_MIN_RATIO ~ HANDLE_SIZE_MAX_RATIO)才算匹配 // 4、凸起本身不算 NG,只有"某个把手区域附近找不到匹配的凸起"才算把手缺失(NG 由 CheckRun 上报) -int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &img, const cv::Mat &bigmask) +int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &bigmask) { m_bHandleDetSucc = false; m_bHandleFound = false; @@ -2589,38 +2589,23 @@ int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &img, const cv::Mat &b } m_bHandleDetSucc = true; - // 2、把手判定参数 + 把手区域(最多4个,非空区域表示该处应该有一个把手) - Handle_Check_Param handleParam; + // 2、把手检测开关 + 把手区域(最多4个,非空区域表示该处应该有一个把手) + bool bDetHandle = false; std::vector regionBoxList; if (m_pFuntion != NULL) { const Function_Support_Det &supportDet = m_pFuntion->function.f_supportDet; - handleParam = supportDet.handleParam; + bDetHandle = supportDet.bDetHandle; for (size_t i = 0; i < supportDet.handleBoxes.size() && i < HANDLE_REGION_MAX_NUM; i++) { regionBoxList.push_back(supportDet.handleBoxes[i]); } } - m_bHandleExpect = (handleParam.bOpen && !regionBoxList.empty()); - m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "protrusion num %ld handle region num %ld expect %s", - protrusionList.size(), regionBoxList.size(), BOOL_TO_STR(m_bHandleExpect)); - m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "%s", handleParam.GetInfo("judge").c_str()); + m_bHandleExpect = (bDetHandle && !regionBoxList.empty()); + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "protrusion num %ld handle region num %ld bDetHandle %s expect %s", + protrusionList.size(), regionBoxList.size(), BOOL_TO_STR(bDetHandle), BOOL_TO_STR(m_bHandleExpect)); - // 灰阶判定是预留项:只有开启时才做灰度换算,避免无谓开销 - cv::Mat grayImg; - if (handleParam.bJudgeGray && !img.empty()) - { - if (img.channels() != 1) - { - cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY); - } - else - { - grayImg = img; - } - } - - // 3、凸起候选实测特征(面积/填充率/灰度差,供日志与后续判定用) + // 3、凸起候选实测特征(面积/填充率,仅用于日志排查) for (size_t i = 0; i < protrusionList.size(); i++) { Handle_Candidate_ cand; @@ -2629,17 +2614,11 @@ int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &img, const cv::Mat &b bool bHorz = (cand.protr.nSide == 0 || cand.protr.nSide == 1); cand.nLen = bHorz ? cand.protr.roi.height : cand.protr.roi.width; cand.nDepth = bHorz ? cand.protr.roi.width : cand.protr.roi.height; - if (!grayImg.empty()) - { - cand.fGrayDiff = CalProtrusionGrayDiff(grayImg, cand.protr.roi); - } m_HandleCandList.push_back(cand); - // grayDiff 打印 -1 表示本次未开启灰阶判定(未计算) m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", - "protrusion %ld side %d roi [%d %d %d %d] len %d depth %d area %0.0f fill %0.2f grayDiff %0.1f", + "protrusion %ld side %d roi [%d %d %d %d] len %d depth %d area %0.0f fill %0.2f", i, cand.protr.nSide, cand.protr.roi.x, cand.protr.roi.y, cand.protr.roi.width, cand.protr.roi.height, - cand.nLen, cand.nDepth, cand.protr.fArea, cand.protr.fFillRatio, - grayImg.empty() ? -1.0f : cand.fGrayDiff); + cand.nLen, cand.nDepth, cand.protr.fArea, cand.protr.fFillRatio); } // 4、逐个把手区域:在区域附近找宽高匹配的凸起(同一个凸起只匹配给一个区域) @@ -2650,7 +2629,7 @@ int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &img, const cv::Mat &b Handle_Region_Result_ result; result.nIdx = (int)ir + 1; result.boxRegion = regionBoxList[ir]; - int nMatchIdx = MatchHandleRegion(m_HandleCandList, usedList, result.boxRegion, handleParam, result.roiHandle); + int nMatchIdx = MatchHandleRegion(m_HandleCandList, usedList, result.boxRegion, result.roiHandle); result.bFound = (nMatchIdx >= 0); if (result.bFound) { @@ -2844,10 +2823,9 @@ int ImgCheckAnalysisy::CheckTagByBigMask(const cv::Mat &img, const cv::Mat &bigm // 在某个把手区域附近找宽高匹配的凸起 // 匹配条件:1、凸起中心落在把手区域附近(区域宽/高各外扩 HANDLE_NEAR_EXPAND_RATIO) // 2、凸起宽高与该区域外接矩形宽高的比值都在 [HANDLE_SIZE_MIN_RATIO, HANDLE_SIZE_MAX_RATIO](允许横竖互换) -// 3、填充率、预留的面积/灰阶等开关条件都满足 // 返回凸起在 candList 中的下标,-1 表示没找到 int ImgCheckAnalysisy::MatchHandleRegion(const std::vector &candList, const std::vector &usedList, - const cv::Rect &boxRegion, const Handle_Check_Param ¶m, cv::Rect &roiHandle) + const cv::Rect &boxRegion, cv::Rect &roiHandle) { if (boxRegion.width <= 0 || boxRegion.height <= 0) { @@ -2890,24 +2868,6 @@ int ImgCheckAnalysisy::MatchHandleRegion(const std::vector &c nNearIdx = (int)i; } - // 类似矩形块:凸起区域内掩膜填充率要高 - if (param.bJudgeFill && cand.protr.fFillRatio < param.fFillMin) - { - continue; - } - // 预留:凸起面积 - if (param.bJudgeArea && param.fAreaMax > param.fAreaMin && - (cand.protr.fArea < param.fAreaMin || cand.protr.fArea > param.fAreaMax)) - { - continue; - } - // 预留:凸起与背景的灰度差 - if (param.bJudgeGray && param.fGrayMax > param.fGrayMin && - (cand.fGrayDiff < param.fGrayMin || cand.fGrayDiff > param.fGrayMax)) - { - continue; - } - // 宽高:与把手区域外接矩形的宽高相近(允许横竖互换) float fw = (float)roi.width; float fh = (float)roi.height; @@ -2948,39 +2908,6 @@ int ImgCheckAnalysisy::MatchHandleRegion(const std::vector &c return nBestIdx; } -// 计算凸起区域与周边背景的平均灰度差(灰阶判定的预留量测值,grayImg 需为单通道) -float ImgCheckAnalysisy::CalProtrusionGrayDiff(const cv::Mat &grayImg, const cv::Rect &roi) -{ - if (grayImg.empty() || grayImg.channels() != 1 || roi.width <= 0 || roi.height <= 0) - { - return 0.0f; - } - // 凸起区域通常整个都在掩膜内,向外扩一圈才能取到背景 - int nExpand = std::max(20, std::min(roi.width, roi.height) / 2); - cv::Rect roiBg(roi.x - nExpand, roi.y - nExpand, roi.width + 2 * nExpand, roi.height + 2 * nExpand); - roiBg &= cv::Rect(0, 0, grayImg.cols, grayImg.rows); - if (roiBg.width <= 0 || roiBg.height <= 0) - { - return 0.0f; - } - - cv::Mat grayRoi = grayImg(roiBg); - cv::Mat maskIn = cv::Mat::zeros(roiBg.size(), CV_8UC1); - cv::Rect inter = roi & roiBg; - cv::rectangle(maskIn, inter - roiBg.tl(), cv::Scalar(255), cv::FILLED); - cv::Mat maskOut; - cv::bitwise_not(maskIn, maskOut); - if (cv::countNonZero(maskIn) <= 0 || cv::countNonZero(maskOut) <= 0) - { - return 0.0f; - } - - double fMeanIn = cv::mean(grayRoi, maskIn)[0]; - double fMeanOut = cv::mean(grayRoi, maskOut)[0]; - double fDiff = fMeanIn - fMeanOut; - return (float)(fDiff >= 0 ? fDiff : -fDiff); -} - int ImgCheckAnalysisy::CalProductSize() { float fw = m_outer_roi.width * m_fImgage_Scale_X; diff --git a/ConfigModule/include/CheckConfigDefine.h b/ConfigModule/include/CheckConfigDefine.h index 21371e7..e182152 100644 --- a/ConfigModule/include/CheckConfigDefine.h +++ b/ConfigModule/include/CheckConfigDefine.h @@ -937,68 +937,23 @@ struct Function_Image_Align // 把手区域最大数量(参数里最多可画4个把手区域) #define HANDLE_REGION_MAX_NUM 4 -// 把手缺失判定参数: -// 判定标准:参数里非空的"把手区域"附近必须存在一个凸起,且凸起宽高与区域宽高相近 -// (比值范围见 ImgCheckAnalysisy 里的 HANDLE_SIZE_MIN_RATIO ~ HANDLE_SIZE_MAX_RATIO) -// 面积/灰阶等参数保留开关,后续需要时再启用 -struct Handle_Check_Param -{ - bool bOpen; // 是否检测把手缺失(false 时不做把手判定,也不报 NG) - // 默认关闭,由 "Support_QX_Detect" 检测项开启时置为 true(可用 handle_disabled 单独关闭) - bool bJudgeFill; // 是否用"类矩形块"填充率判定 - float fFillMin; // 填充率下限 - bool bJudgeArea; // 预留:凸起面积判定 - float fAreaMin; - float fAreaMax; - bool bJudgeGray; // 预留:凸起与背景灰度差判定 - float fGrayMin; - float fGrayMax; - - Handle_Check_Param() - { - Init(); - } - void Init() - { - bOpen = false; - bJudgeFill = true; - fFillMin = 0.75f; - bJudgeArea = false; - fAreaMin = 0; - fAreaMax = 0; - bJudgeGray = false; - fGrayMin = 0; - fGrayMax = 0; - } - void copy(Handle_Check_Param tem) - { - // 全为基本类型,直接赋值(避免后续加字段时漏拷) - *this = tem; - } - std::string GetInfo(std::string str) - { - char buffer[256]; - sprintf(buffer, "%s>>bOpen %d fill[%d %0.2f] area[%d %0.0f %0.0f] gray[%d %0.0f %0.0f]\n", - str.c_str(), bOpen, bJudgeFill, fFillMin, - bJudgeArea, fAreaMin, fAreaMax, bJudgeGray, fGrayMin, fGrayMax); - std::string str123 = buffer; - return str123; - } -}; - // 支架检测 struct Function_Support_Det { bool bOpen; // 是否开启 + // 支架参数 + bool bDetSupport; std::vector supportRegion; cv::RotatedRect supportRect; float x_offset; float y_offset; float r_offset; // 把手区域(最多 HANDLE_REGION_MAX_NUM 个,只保存非空的;区域宽高用于把手卡控) + bool bDetHandle; // 把手缺失检测开关 std::vector> handleRegions; std::vector handleBoxes; - Handle_Check_Param handleParam; // 把手缺失判定参数 + // 标签参数 + bool bDetTag; Function_Support_Det () { @@ -1008,40 +963,42 @@ struct Function_Support_Det void Init() { bOpen = false; + bDetSupport = false; supportRegion.clear(); supportRect = cv::RotatedRect(); x_offset = 0; y_offset = 0; r_offset = 0; + bDetHandle = false; handleRegions.clear(); handleBoxes.clear(); - handleParam.Init(); + bDetTag = false; } void copy(Function_Support_Det tem) { this->bOpen = tem.bOpen; + this->bDetSupport = tem.bDetSupport; this->supportRegion.assign(tem.supportRegion.begin(), tem.supportRegion.end()); this->supportRect = tem.supportRect; this->x_offset = tem.x_offset; this->y_offset = tem.y_offset; this->r_offset = tem.r_offset; + this->bDetHandle = tem.bDetHandle; this->handleRegions.assign(tem.handleRegions.begin(), tem.handleRegions.end()); this->handleBoxes.assign(tem.handleBoxes.begin(), tem.handleBoxes.end()); - this->handleParam.copy(tem.handleParam); + this->bDetTag = tem.bDetTag; } void print(std::string str) { - printf("%s>>bOpen %d x_offset %f y_offset %f r_offset %f handleRegion num %ld\n", str.c_str(), - bOpen, x_offset, y_offset, r_offset, handleRegions.size()); - printf("%s", handleParam.GetInfo("handleParam").c_str()); + printf("%s>>bOpen %d bDetSupport %d bDetHandle %d bDetTag %d x_offset %f y_offset %f r_offset %f handleRegion num %ld\n", + str.c_str(), bOpen, bDetSupport, bDetHandle, bDetTag, x_offset, y_offset, r_offset, handleRegions.size()); } std::string GetInfo(std::string str) { char buffer[256]; - sprintf(buffer, "%s>>bOpen %d x_offset %f y_offset %f r_offset %f handleRegion num %ld\n", str.c_str(), - bOpen, x_offset, y_offset, r_offset, handleRegions.size()); + sprintf(buffer, "%s>>bOpen %d bDetSupport %d bDetHandle %d bDetTag %d x_offset %f y_offset %f r_offset %f handleRegion num %ld\n", + str.c_str(), bOpen, bDetSupport, bDetHandle, bDetTag, x_offset, y_offset, r_offset, handleRegions.size()); std::string str123 = buffer; - str123 += handleParam.GetInfo("handleParam"); return str123; } diff --git a/ConfigModule/src/JsonConfig.cpp b/ConfigModule/src/JsonConfig.cpp index 2b91b64..7cf0b24 100644 --- a/ConfigModule/src/JsonConfig.cpp +++ b/ConfigModule/src/JsonConfig.cpp @@ -421,6 +421,11 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct { // 支架参数 { + if (value_f["form"]["support_param"]["bDetSupport"]) + { + function.f_supportDet.bDetSupport = value_f["form"]["support_param"]["bDetSupport"].asBool(); + } + auto value_region = value_f["form"]["support_param"]["support_region"]; if (value_region.isArray()) { @@ -437,22 +442,26 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct function.f_supportDet.supportRect = minAreaRect(function.f_supportDet.supportRegion); } } + + if (value_f["form"]["support_param"]["x_offset"]) + { + function.f_supportDet.x_offset = value_f["form"]["support_param"]["x_offset"].asFloat(); + } + if (value_f["form"]["support_param"]["y_offset"]) + { + function.f_supportDet.y_offset = value_f["form"]["support_param"]["y_offset"].asFloat(); + } + if (value_f["form"]["support_param"]["r_offset"]) + { + function.f_supportDet.r_offset = value_f["form"]["support_param"]["r_offset"].asFloat(); + } } - if (value_f["form"]["support_param"]["x_offset"]) - { - function.f_supportDet.x_offset = value_f["form"]["support_param"]["x_offset"].asFloat(); - } - if (value_f["form"]["support_param"]["y_offset"]) - { - function.f_supportDet.y_offset = value_f["form"]["support_param"]["y_offset"].asFloat(); - } - if (value_f["form"]["support_param"]["r_offset"]) - { - function.f_supportDet.r_offset = value_f["form"]["support_param"]["r_offset"].asFloat(); - } - // 把手参数 { + if (value_f["form"]["handle_param"]["bDetHandle"]) + { + function.f_supportDet.bDetHandle = value_f["form"]["handle_param"]["bDetHandle"].asBool(); + } auto handle_p = value_f["form"]["handle_param"]; bool bNewFormat = handle_p["handle_region1"].isArray(); for (int idx = 0; idx < HANDLE_REGION_MAX_NUM; idx++) @@ -493,48 +502,11 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct function.f_supportDet.handleBoxes.push_back(cv::boundingRect(region)); } } - - // 把手缺失判定参数 + // 标签参数 { - auto handle_p = value_f["form"]["handle_param"]; - Handle_Check_Param &handleParam = function.f_supportDet.handleParam; - // "Support_QX_Detect" 检测项开启时,默认做把手缺失检测 - handleParam.bOpen = true; - if (handle_p["handle_disabled"].isBool()) - { - handleParam.bOpen = !handle_p["handle_disabled"].asBool(); - } - if (handle_p["handle_fill_disabled"].isBool()) - { - handleParam.bJudgeFill = !handle_p["handle_fill_disabled"].asBool(); - } - if (handle_p["handle_fill_min"].isNumeric()) - { - handleParam.fFillMin = handle_p["handle_fill_min"].asFloat(); - } - if (handle_p["handle_area_disabled"].isBool()) - { - handleParam.bJudgeArea = !handle_p["handle_area_disabled"].asBool(); - } - if (handle_p["handle_area_min"].isNumeric()) - { - handleParam.fAreaMin = handle_p["handle_area_min"].asFloat(); - } - if (handle_p["handle_area_max"].isNumeric()) - { - handleParam.fAreaMax = handle_p["handle_area_max"].asFloat(); - } - if (handle_p["handle_gray_disabled"].isBool()) - { - handleParam.bJudgeGray = !handle_p["handle_gray_disabled"].asBool(); - } - if (handle_p["handle_gray_min"].isNumeric()) - { - handleParam.fGrayMin = handle_p["handle_gray_min"].asFloat(); - } - if (handle_p["handle_gray_max"].isNumeric()) + if (value_f["form"]["tag_param"]["bDetTag"]) { - handleParam.fGrayMax = handle_p["handle_gray_max"].asFloat(); + function.f_supportDet.bDetTag = value_f["form"]["tag_param"]["bDetTag"].asBool(); } } }