diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 2ac3ca5..8d3a6eb 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -16,6 +16,12 @@ #include #include +// ===================== Pin 整体偏移估计参数 ===================== +static const double PIN_SHIFT_MIN_SUPPORT_RATIO = 0.80; // 判定整体偏移所需的最小支持度(支持pin数 / 模板pin总数) +static const float PIN_SHIFT_BIN_SIZE_PX = 4.0f; // 位移投票的网格尺寸(px), 同时作为峰值邻域半径 +static const float PIN_SHIFT_MAX_GAP_RATIO = 0.5f; // 整体位移量级上限 = 相邻pin最小间距 * 该比例 +// ====================================================================================== + // 分类映射配置文件(每行:label索引:缺陷名称;冒号后名称为该缺陷类型名称,行的顺序即缺陷类型索引) static const std::string CLS_LABEL_CONFIG_FILE = "/home/aidlux/BOE/UseModel_ZB/KK_cls.txt"; @@ -820,9 +826,7 @@ int ImgCheckAnalysisy::CheckRun() m_outer_roi = curOuterBound; } #else - // 检测PIN偏移/缺失对位置基准最敏感:PIN 位置的真实基准是产品内腔(内定位), - // 因此优先用内定位计算仿射(当前内边缘 -> 模板内边缘);模板/当前内定位无效时回退外定位。 - // 注意: 这里不再对原图做 warpAffine, 只计算矩阵, 并把模板 Pin 轮廓反变换到当前图坐标系。 + // 不对原图做 warpAffine, 只计算矩阵, 并把模板 Pin 轮廓反变换到当前图坐标系。 bool bUseInnerAlign = (m_tplInnerRect.size.width > 0 && m_tplInnerRect.size.height > 0 && m_inner_rroi.size.width > 0 && m_inner_rroi.size.height > 0); if (bUseInnerAlign) @@ -2253,14 +2257,13 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img) Mat pin_mask; int re = m_Pin_QX_Det.GetPinMask(img, &m_Edge_DetConfig, pin_mask); - // 统计tplPinMask的blob边缘点,并绘制到tplImg上 + // 获取当前pinmask vector> pin_contours; vector hierarchy; cv::findContours(pin_mask, pin_contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); //缩放比例 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++) @@ -2312,6 +2315,129 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img) } std::vector curUsed(nCur, false); + // 整体偏移估计与消除(阈值见文件顶部 PIN_SHIFT_* 常量): + // 若足够多的模板 pin 都具有方向一致、量级接近的位移, 则把模板 pin 轮廓整体平移到与检测 pin 一致的位置, + // 后续匹配/偏移判据均基于平移后的模板 pin + { + // 相邻 pin 最小间距: 用于限制整体偏移的量级(整体偏移不应接近一个 pin 间距, 否则无法与"错配一列"区分) + float minGap = 0.0f; + for (int i = 0; i < nTpl; i++) + { + for (int j = i + 1; j < nTpl; j++) + { + float d = static_cast(cv::norm(tplCenters[i] - tplCenters[j])); + if (minGap <= 0.0f || d < minGap) + { + minGap = d; + } + } + } + + if (nCur > 0) + { + // 1) 每个模板 pin 取最近检测 pin 的位移作为候选位移(最近检测 pin 必定存在, nCur > 0) + std::vector disp(nTpl); + for (int i = 0; i < nTpl; i++) + { + int nearestIdx = 0; + double bestD = 1e12; + for (int j = 0; j < nCur; j++) + { + double d = cv::norm(curCenters[j] - tplCenters[i]); + if (d < bestD) + { + bestD = d; + nearestIdx = j; + } + } + disp[i] = curCenters[nearestIdx] - tplCenters[i]; + } + + // 2) 网格投票找主位移(峰值并列时优先取量级更小的) + const float binSize = PIN_SHIFT_BIN_SIZE_PX; + std::map, int> hist; + int bestCount = 0; + float bestX = 0.0f; + float bestY = 0.0f; + for (int i = 0; i < nTpl; i++) + { + std::pair key(static_cast(std::lround(disp[i].x / binSize)), + static_cast(std::lround(disp[i].y / binSize))); + int cnt = ++hist[key]; + float cx = key.first * binSize; + float cy = key.second * binSize; + if (cnt > bestCount || + (cnt == bestCount && cx * cx + cy * cy < bestX * bestX + bestY * bestY)) + { + bestCount = cnt; + bestX = cx; + bestY = cy; + } + } + + // 3) 峰值邻域(±1 档)内的候选位移取均值, 同时统计支持度 + double sumX = 0.0; + double sumY = 0.0; + int sumN = 0; + for (int i = 0; i < nTpl; i++) + { + if (std::fabs(disp[i].x - bestX) <= binSize && + std::fabs(disp[i].y - bestY) <= binSize) + { + sumX += disp[i].x; + sumY += disp[i].y; + sumN++; + } + } + + cv::Point2f globalShift(0.0f, 0.0f); + if (sumN > 0) + { + globalShift.x = static_cast(sumX / sumN); + globalShift.y = static_cast(sumY / sumN); + } + else + { + globalShift.x = bestX; + globalShift.y = bestY; + } + + // 4) 支持度达标 且 位移量级小于 pin 间距的限定比例 -> 判定为整体偏移 + const double supportRatio = static_cast(sumN) / nTpl; + const float shiftLen = static_cast(cv::norm(globalShift)); + const bool bGlobalShift = (supportRatio >= PIN_SHIFT_MIN_SUPPORT_RATIO) && + (minGap <= 0.0f || shiftLen < minGap * PIN_SHIFT_MAX_GAP_RATIO); + + if (bGlobalShift) + { + // 模板 pin 轮廓整体平移, 使其与检测 pin 一致 + const int shiftX = cvRound(globalShift.x); + const int shiftY = cvRound(globalShift.y); + for (int i = 0; i < nTpl; i++) + { + for (auto &pt : m_tplPinContoursCur[i]) + { + pt.x += shiftX; + pt.y += shiftY; + } + // 与前面的统计口径保持一致: 中心/外包框由平移后的轮廓重新计算 + tplCenters[i] = getCenter(m_tplPinContoursCur[i]); + tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]); + } + m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", + "global offset removed: template pins shifted dx %.2f dy %.2f mm (%.1f %.1f px), support %d/%d", + globalShift.x * m_fImgage_Scale_X, globalShift.y * m_fImgage_Scale_Y, + globalShift.x, globalShift.y, sumN, nTpl); + } + else if (bestCount > 0) + { + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Pin Check", + "global offset not applied: dx %.1f dy %.1f px, support %d/%d (ratio %.2f, pin gap %.1f px)", + globalShift.x, globalShift.y, sumN, nTpl, supportRatio, minGap); + } + } + } + // 3、偏移阈值直接使用基础参数 pin_x_offset / pin_y_offset (mm),与换算为 mm 的偏移量直接比较 float offsetThX = 0.0f; // mm float offsetThY = 0.0f; // mm