diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 8af85b4..da85e02 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -476,6 +476,38 @@ int ImgCheckAnalysisy::CheckRun() float diff_align = std::fabs(aouter - ainner); diff_align = std::fmod(diff_align, 180.0f); // 模 180 度 + // 两条直线的夹角取锐角:min(diff, 180 - diff) + if (diff_align > 90.0f) + { + diff_align = 180.0f - diff_align; + } + + // 如果diff_align大于10,则添加到缺陷列表 + if (diff_align > 10.0f) + { + QX_ERROR_INFO_ alignErr; + alignErr.Idx = static_cast(m_pDetResult->pQx_ErrorList->size()); + + // roi 用整张检测图区域(detImg 坐标系) + cv::Rect tplRect = m_tplOuterRect.boundingRect(); + alignErr.roi = cv::Rect(0, 0, tplRect.width, tplRect.height); + alignErr.area = alignErr.roi.width * alignErr.roi.height; + alignErr.JudgArea = alignErr.area * m_fImgage_Scale_X * m_fImgage_Scale_Y; + alignErr.flen = tplRect.width * m_fImgage_Scale_X; + alignErr.fbreadth = tplRect.height * m_fImgage_Scale_Y; + + alignErr.nconfig_qx_type = CONFIG_QX_NAME_cell_zangwu; + alignErr.qx_name = CONFIG_QX_NAME_Names[CONFIG_QX_NAME_cell_zangwu]; + alignErr.grayDis = diff_align; // 记录内外边缘夹角(度) + alignErr.detRegionidxList.push_back(0); + + alignErr.detlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Align Check", + "outer-inner angle diff %.2f deg", diff_align); + m_pDetResult->pQx_ErrorList->push_back(alignErr); + + m_pdetlog->AddCheckstr(PrintLevel_0, "Align Check", "angle diff %.2f deg > 10", diff_align); + } + /*投影对齐模板*/ Adapt_Config(m_tplOuterRect, m_outer_rroi, m_CheckResult_shareP->in_shareImage->img); m_outer_roi = m_tplOuterRect.boundingRect(); @@ -1854,6 +1886,7 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img) const float pin_scale_x = (pin_mask.cols > 0) ? static_cast(img.cols) / pin_mask.cols : 0.0f; const float pin_scale_y = (pin_mask.rows > 0) ? static_cast(img.rows) / pin_mask.rows : 0.0f; // 将轮廓点从 mask 坐标系 映射回 tplImg 坐标系 + m_curPinContours.clear(); m_curPinContours.reserve(pin_contours.size()); for (size_t i = 0; i < pin_contours.size(); i++) { @@ -1867,6 +1900,123 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img) m_curPinContours.push_back(mapped); } + // 根据模板Pin和当前Pin轮廓进行对比,判断当前有无缺失和偏移 + { + // 计算轮廓中心点(外接矩形中心) + auto getCenter = [](const std::vector &contour) -> cv::Point2f + { + cv::Rect r = cv::boundingRect(contour); + return cv::Point2f(r.x + r.width * 0.5f, r.y + r.height * 0.5f); + }; + + const int nTpl = static_cast(m_tplPinContours.size()); + const int nCur = static_cast(m_curPinContours.size()); + + if (nTpl > 0) + { + // 1、统计模板 pin 中心与平均最小边长,用于自适应阈值 + std::vector tplCenters(nTpl); + double sumMinDim = 0.0; + for (int i = 0; i < nTpl; i++) + { + tplCenters[i] = getCenter(m_tplPinContours[i]); + cv::Rect r = cv::boundingRect(m_tplPinContours[i]); + sumMinDim += (r.width < r.height) ? r.width : r.height; + } + const float avgMinDim = static_cast(sumMinDim / nTpl); + + // 偏移阈值约 1/4 个 pin 尺寸,缺失阈值约 3/4 个 pin 尺寸 + float offsetTh = avgMinDim * 0.5f; + if (offsetTh < 3.0f) + { + offsetTh = 3.0f; + } + float missTh = avgMinDim * 0.75f; + if (missTh < offsetTh + 1.0f) + { + missTh = offsetTh + 1.0f; + } + + // 2、当前 pin 中心 + std::vector curCenters(nCur); + for (int i = 0; i < nCur; i++) + { + curCenters[i] = getCenter(m_curPinContours[i]); + } + std::vector curUsed(nCur, false); + + // 上报缺失/偏移缺陷(缺陷类型可按需调整) + auto reportPinError = [&](int pinIdx, const cv::Point2f &tplCenter, const std::string &reason) + { + QX_ERROR_INFO_ pinErr; + pinErr.Idx = static_cast(m_pDetResult->pQx_ErrorList->size()); + cv::Rect r = cv::boundingRect(m_tplPinContours[pinIdx]); + pinErr.roi = r; + pinErr.area = static_cast(cv::contourArea(m_tplPinContours[pinIdx])); + pinErr.JudgArea = pinErr.area * m_fImgage_Scale_X * m_fImgage_Scale_Y; + int longSide = (r.width > r.height) ? r.width : r.height; + int shortSide = (r.width < r.height) ? r.width : r.height; + pinErr.flen = longSide * m_fImgage_Scale_X; + pinErr.fbreadth = shortSide * m_fImgage_Scale_Y; + pinErr.nconfig_qx_type = CONFIG_QX_NAME_cell_ymhs; + pinErr.qx_name = CONFIG_QX_NAME_Names[CONFIG_QX_NAME_cell_ymhs]; + pinErr.detRegionidxList.push_back(0); + pinErr.detlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Pin Check", + "pin %d center(%d,%d) %s", + pinIdx, static_cast(tplCenter.x), static_cast(tplCenter.y), + reason.c_str()); + m_pDetResult->pQx_ErrorList->push_back(pinErr); + }; + + int nMiss = 0; + int nOffset = 0; + + // 3、逐个模板 pin 找最近且未被占用的当前 pin + for (int i = 0; i < nTpl; i++) + { + int bestIdx = -1; + double bestDist = 1e12; + for (int j = 0; j < nCur; j++) + { + if (curUsed[j]) + { + continue; + } + float dx = tplCenters[i].x - curCenters[j].x; + float dy = tplCenters[i].y - curCenters[j].y; + double d = std::sqrt(static_cast(dx) * dx + static_cast(dy) * dy); + if (d < bestDist) + { + bestDist = d; + bestIdx = j; + } + } + + // 无候选或距离过远 -> 缺失 + if (bestIdx < 0 || bestDist > missTh) + { + nMiss++; + reportPinError(i, tplCenters[i], "miss"); + m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "pin %d MISS", i); + continue; + } + + curUsed[bestIdx] = true; + + // 距离超出偏移阈值 -> 偏移 + if (bestDist > offsetTh) + { + nOffset++; + char buf[64]; + snprintf(buf, sizeof(buf), "offset %.1fpx", bestDist); + reportPinError(i, tplCenters[i], buf); + m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "pin %d OFFSET %.1fpx", i, bestDist); + } + } + + m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "tpl %d cur %d miss %d offset %d", nTpl, nCur, nMiss, nOffset); + } + } if (m_Edge_DetConfig.bSaveResultImg) {