From 58563d817c166f861a112b44eff8a62c16801c5d Mon Sep 17 00:00:00 2001 From: liusiyang Date: Fri, 9 Oct 2026 22:07:00 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E6=94=AF=E6=9E=B6=E5=81=8F=E7=A7=BB?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=AE=A1=E7=AE=97=E5=88=B0=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E8=BE=B9=E7=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AlgorithmModule/src/AI_Edge_Algin.cpp | 1 - AlgorithmModule/src/ImgCheckAnalysisy.cpp | 231 ++++++++++++++++------ ConfigModule/include/CheckConfigDefine.h | 17 +- ConfigModule/src/JsonConfig.cpp | 19 ++ 4 files changed, 201 insertions(+), 67 deletions(-) diff --git a/AlgorithmModule/src/AI_Edge_Algin.cpp b/AlgorithmModule/src/AI_Edge_Algin.cpp index c9ece66..a55c608 100644 --- a/AlgorithmModule/src/AI_Edge_Algin.cpp +++ b/AlgorithmModule/src/AI_Edge_Algin.cpp @@ -295,7 +295,6 @@ int AI_Edge_Algin::Detect(const cv::Mat &img, DetConfig *pDetConfig, std::shared m_pCheckResult_Aling->bigmask = Big_outimg; m_pCheckResult_Aling->smallmask = Small_outimg; m_pCheckResult_Aling->tagmask = Tag_outimg; - pCheckResult_Aling = m_pCheckResult_Aling; return 0; diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 819c856..c76f5ad 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -884,82 +884,189 @@ int ImgCheckAnalysisy::CheckRun() m_pdetlog->AddCheckstr(PrintLevel_0, "3、pre detect", "-------------------------pre Det--------------- \n"); - /* 检测支架偏移(bDetSupport 开关关闭时不做支架判定) */ + /* 检测支架偏移(bDetSupport 开关关闭时不做支架判定) + 不使用参数绘制区域,结合两次定位的 mask 计算: + 1、bigmask 最大连通域 = 产品区域 rp(近似矩形,四周可能有凸起) + 2、smallmask 最大连通域 = 支架区域 rs(按定位裁剪区 m_outer_rroi 外接矩形映射回原图坐标) + 3、按 edge_select 指定方向(left/right/up/down)计算 rs 中心到 rp 对应边缘的距离 + 4、计算值与标准值(x_standard/y_standard)做差取平均,平均差超过 offset(x_offset) → 偏移 */ { Function_Support_Det &supportDet = m_pFuntion->function.f_supportDet; - if (supportDet.bOpen && supportDet.bDetSupport && - supportDet.supportRect.size.width > 0 && supportDet.supportRect.size.height > 0) - { - cv::RotatedRect &inner = m_inner_rroi; - cv::RotatedRect &support = supportDet.supportRect; - - // x、y 中心偏移 - float dx = fabs(inner.center.x - support.center.x); - float dy = fabs(inner.center.y - support.center.y); - // 旋转偏移(矩形旋转 90 度等价,角度取最小差) - float dangle = fabs(inner.angle - support.angle); - if (dangle > 45.0f) + if (supportDet.bOpen && supportDet.bDetSupport) + { + const cv::Mat &bigmask = m_pEdge_Align_Result->bigmask; + const cv::Mat &smallmask = m_pEdge_Align_Result->smallmask; + if (bigmask.empty() || smallmask.empty()) { - dangle = fabs(dangle - 90.0f); + m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "mask empty (big %d small %d), skip support offset", bigmask.empty(), smallmask.empty()); } - - bool bOffset = (dx > supportDet.x_offset) || (dy > supportDet.y_offset) || (dangle > supportDet.r_offset); - - // 缺失判定:内边缘面积小于支架模板面积的 75% - float innerArea = inner.size.width * inner.size.height; - float supportArea = support.size.width * support.size.height; - bool bLoss = (supportArea > 0.0f) && (innerArea < supportArea * 0.75f); - - if (bOffset || bLoss) + else { - QX_ERROR_INFO_ temerror; - temerror.Idx = m_pDetResult->pQx_ErrorList->size(); - - // roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系 - cv::Rect roi = MatAffineRect(m_matSrc2Det, inner.boundingRect()); - roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows); - temerror.roi = roi; - temerror.area = roi.width * roi.height; - temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y; - temerror.JudgArea_second = temerror.JudgArea; - float w = inner.size.width; - float h = inner.size.height; - temerror.flen = (w > h ? w : h) * m_fImgage_Scale_X; - temerror.fbreadth = (w > h ? h : w) * m_fImgage_Scale_Y; - - if (bLoss) + // 1、mask 二值化(兼容 0/1 与 0/255 掩膜) + cv::Mat bigBin, smallBin; { - temerror.nconfig_qx_type = CONFIG_QX_NAME_support_loss; + cv::Mat grayMask; + if (bigmask.channels() != 1) + { + cv::cvtColor(bigmask, grayMask, cv::COLOR_BGR2GRAY); + } + else + { + grayMask = bigmask; + } + double dMin = 0; + double dMax = 0; + cv::minMaxLoc(grayMask, &dMin, &dMax); + cv::threshold(grayMask, bigBin, dMax > 1 ? dMax * 0.5 : 0, 255, cv::THRESH_BINARY); } - else { - temerror.nconfig_qx_type = CONFIG_QX_NAME_support_offset; + cv::Mat grayMask; + if (smallmask.channels() != 1) + { + cv::cvtColor(smallmask, grayMask, cv::COLOR_BGR2GRAY); + } + else + { + grayMask = smallmask; + } + double dMin = 0; + double dMax = 0; + cv::minMaxLoc(grayMask, &dMin, &dMax); + cv::threshold(grayMask, smallBin, dMax > 1 ? dMax * 0.5 : 0, 255, cv::THRESH_BINARY); } - temerror.qx_name = CONFIG_QX_NAME_Names[temerror.nconfig_qx_type]; - temerror.result = QX_RESULT_TYPE_NG; - temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG]; - // 计算支架中心所在的检测区域 - cv::Point pCenter; - pCenter.x = roi.x + roi.width * 0.5; - pCenter.y = roi.y + roi.height * 0.5; - int nmaxregionIdx = 0; - for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++) + // 2、最大连通域轮廓:bigmask -> 产品区域 rp(原图坐标);smallmask -> 支架区域 rs(smallmask 局部坐标) + bool bFoundRp = false; + std::vector rpContour = CheckUtil::getLargestContourROI(bigBin, bFoundRp); + bool bFoundRs = false; + std::vector rsContour = CheckUtil::getLargestContourROI(smallBin, bFoundRs); + if (!bFoundRp || rpContour.size() < 3 || !bFoundRs || rsContour.size() < 3) { - const std::vector &polygon = m_DetRoiList.roiList_Src[iregion]; - double result = cv::pointPolygonTest(polygon, pCenter, false); - if (result < 0) - { - continue; - } - nmaxregionIdx = iregion; + m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "contour invalid (rp %d %ld, rs %d %ld), skip support offset", + bFoundRp, rpContour.size(), bFoundRs, rsContour.size()); } - temerror.detRegionidxList.push_back(nmaxregionIdx); + else + { + // rp 边缘:产品最大连通域的外接矩形(含凸起) + cv::Rect rpRect = cv::boundingRect(rpContour); + + // rs 映射回原图坐标:smallmask 对应定位时裁剪的大轮廓区域(m_outer_rroi 外接矩形),按比例映射 + cv::Rect bigRect = m_outer_rroi.boundingRect(); + float fx = bigRect.width * 1.0f / smallmask.cols; + float fy = bigRect.height * 1.0f / smallmask.rows; + cv::Rect rsSmallRect = cv::boundingRect(rsContour); + cv::Rect rsRect( + cvRound(bigRect.x + rsSmallRect.x * fx), + cvRound(bigRect.y + rsSmallRect.y * fy), + cvRound(rsSmallRect.width * fx), + cvRound(rsSmallRect.height * fy)); + + // 3、rs 中心到 rp 上下左右边缘的距离 + float fCx = rsRect.x + rsRect.width * 0.5f; + float fCy = rsRect.y + rsRect.height * 0.5f; + float fDL = fCx - rpRect.x; // 到左边缘 + float fDR = (rpRect.x + rpRect.width) - fCx; // 到右边缘 + float fDT = fCy - rpRect.y; // 到上边缘 + float fDB = (rpRect.y + rpRect.height) - fCy; // 到下边缘 + + // 4、按 edge_select 指定方向(left/right/up/down)取计算值,与标准值做差取平均,平均差超过 offset → 偏移 + // left/right 用 x_standard,up/down 用 y_standard;offset 用 x_offset + float fSumDiff = 0; + int nDiffNum = 0; + std::string strSelect = ""; + std::string strDiff = ""; + char strTmp[64]; + for (size_t iselect = 0; iselect < supportDet.edge_select.size(); iselect++) + { + const std::string &strEdge = supportDet.edge_select[iselect]; + float fDist = 0; + float fStandard = 0; + if (strEdge == "left") + { + fDist = fDL; + fStandard = supportDet.x_standard; + } + else if (strEdge == "right") + { + fDist = fDR; + fStandard = supportDet.x_standard; + } + else if (strEdge == "up") + { + fDist = fDT; + fStandard = supportDet.y_standard; + } + else if (strEdge == "down") + { + fDist = fDB; + fStandard = supportDet.y_standard; + } + else + { + continue; + } - m_pDetResult->pQx_ErrorList->push_back(temerror); + float fDiff = fabs(fDist - fStandard); + fSumDiff += fDiff; + nDiffNum++; + sprintf(strTmp, "%s(%.1f/%.1f) ", strEdge.c_str(), fDist, fStandard); + strSelect += strTmp; + sprintf(strTmp, "%.1f ", fDiff); + strDiff += strTmp; + } + // edge_select 为空或无效时 nDiffNum=0,结果不参与判定(仅打日志) + float fAvgDiff = (nDiffNum > 0) ? (fSumDiff / nDiffNum) : 0.0f; + bool bOffset = (nDiffNum > 0) && (fAvgDiff > supportDet.x_offset); + + m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", + "rp [%d %d %d %d] rs [%d %d %d %d] scale (%.4f %.4f) center (%.1f %.1f) dL %.1f dR %.1f dT %.1f dB %.1f | select %s diff %s avgDiff %.1f limit %.1f ng %d", + rpRect.x, rpRect.y, rpRect.width, rpRect.height, + rsRect.x, rsRect.y, rsRect.width, rsRect.height, + fx, fy, fCx, fCy, fDL, fDR, fDT, fDB, + strSelect.c_str(), strDiff.c_str(), fAvgDiff, supportDet.x_offset, bOffset); + + if (bOffset) + { + QX_ERROR_INFO_ temerror; + temerror.Idx = m_pDetResult->pQx_ErrorList->size(); + + // roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系 + cv::Rect roi = MatAffineRect(m_matSrc2Det, rsRect); + roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows); + temerror.roi = roi; + temerror.area = roi.width * roi.height; + temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y; + temerror.JudgArea_second = temerror.JudgArea; + float w = rsRect.width; + float h = rsRect.height; + temerror.flen = (w > h ? w : h) * m_fImgage_Scale_X; + temerror.fbreadth = (w > h ? h : w) * m_fImgage_Scale_Y; + + temerror.nconfig_qx_type = CONFIG_QX_NAME_support_offset; + temerror.qx_name = CONFIG_QX_NAME_Names[temerror.nconfig_qx_type]; + temerror.result = QX_RESULT_TYPE_NG; + temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG]; + + // 计算支架中心所在的检测区域 + cv::Point pCenter; + pCenter.x = roi.x + roi.width * 0.5; + pCenter.y = roi.y + roi.height * 0.5; + int nmaxregionIdx = 0; + for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++) + { + const std::vector &polygon = m_DetRoiList.roiList_Src[iregion]; + double result = cv::pointPolygonTest(polygon, pCenter, false); + if (result < 0) + { + continue; + } + nmaxregionIdx = iregion; + } + temerror.detRegionidxList.push_back(nmaxregionIdx); - m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "%s %d roi [%d %d %d %d] dx %f dy %f dangle %f", - temerror.qx_name.c_str(), temerror.Idx, roi.x, roi.y, roi.width, roi.height, dx, dy, dangle); + m_pDetResult->pQx_ErrorList->push_back(temerror); + } + } } } } diff --git a/ConfigModule/include/CheckConfigDefine.h b/ConfigModule/include/CheckConfigDefine.h index e182152..5ad2620 100644 --- a/ConfigModule/include/CheckConfigDefine.h +++ b/ConfigModule/include/CheckConfigDefine.h @@ -945,6 +945,9 @@ struct Function_Support_Det bool bDetSupport; std::vector supportRegion; cv::RotatedRect supportRect; + float x_standard; // 水平方向标准距离(left/right 用) + float y_standard; // 垂直方向标准距离(up/down 用) + std::vector edge_select; // 参与计算的方向:up/down/left/right float x_offset; float y_offset; float r_offset; @@ -966,6 +969,9 @@ struct Function_Support_Det bDetSupport = false; supportRegion.clear(); supportRect = cv::RotatedRect(); + x_standard = 0; + y_standard = 0; + edge_select.clear(); x_offset = 0; y_offset = 0; r_offset = 0; @@ -980,6 +986,9 @@ struct Function_Support_Det this->bDetSupport = tem.bDetSupport; this->supportRegion.assign(tem.supportRegion.begin(), tem.supportRegion.end()); this->supportRect = tem.supportRect; + this->x_standard = tem.x_standard; + this->y_standard = tem.y_standard; + this->edge_select.assign(tem.edge_select.begin(), tem.edge_select.end()); this->x_offset = tem.x_offset; this->y_offset = tem.y_offset; this->r_offset = tem.r_offset; @@ -990,14 +999,14 @@ struct Function_Support_Det } void print(std::string 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()); + printf("%s>>bOpen %d bDetSupport %d bDetHandle %d bDetTag %d x_standard %f y_standard %f x_offset %f y_offset %f r_offset %f edge num %ld handleRegion num %ld\n", + str.c_str(), bOpen, bDetSupport, bDetHandle, bDetTag, x_standard, y_standard, x_offset, y_offset, r_offset, edge_select.size(), handleRegions.size()); } std::string GetInfo(std::string str) { char buffer[256]; - 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()); + sprintf(buffer, "%s>>bOpen %d bDetSupport %d bDetHandle %d bDetTag %d x_standard %f y_standard %f x_offset %f y_offset %f r_offset %f edge num %ld handleRegion num %ld\n", + str.c_str(), bOpen, bDetSupport, bDetHandle, bDetTag, x_standard, y_standard, x_offset, y_offset, r_offset, edge_select.size(), handleRegions.size()); std::string str123 = buffer; return str123; } diff --git a/ConfigModule/src/JsonConfig.cpp b/ConfigModule/src/JsonConfig.cpp index 7cf0b24..fec6102 100644 --- a/ConfigModule/src/JsonConfig.cpp +++ b/ConfigModule/src/JsonConfig.cpp @@ -443,6 +443,25 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct } } + if (value_f["form"]["support_param"]["x_standard"]) + { + function.f_supportDet.x_standard = value_f["form"]["support_param"]["x_standard"].asFloat(); + } + if (value_f["form"]["support_param"]["y_standard"]) + { + function.f_supportDet.y_standard = value_f["form"]["support_param"]["y_standard"].asFloat(); + } + + auto value_qx = value_f["form"]["support_param"]["edge_select"]; + if (value_qx.isArray()) + { + for (int idx = 0; idx < value_qx.size(); idx++) + { + std::string channel = value_qx[idx].asString(); + function.f_supportDet.edge_select.push_back(channel); + } + } + if (value_f["form"]["support_param"]["x_offset"]) { function.f_supportDet.x_offset = value_f["form"]["support_param"]["x_offset"].asFloat();