update 初步偏移检测

dev_lsy
xiewenji 1 month ago
parent 37d050139c
commit fcc8fd70c7

@ -476,6 +476,38 @@ int ImgCheckAnalysisy::CheckRun()
float diff_align = std::fabs(aouter - ainner); float diff_align = std::fabs(aouter - ainner);
diff_align = std::fmod(diff_align, 180.0f); // 模 180 度 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<int>(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); Adapt_Config(m_tplOuterRect, m_outer_rroi, m_CheckResult_shareP->in_shareImage->img);
m_outer_roi = m_tplOuterRect.boundingRect(); 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<float>(img.cols) / pin_mask.cols : 0.0f; const float pin_scale_x = (pin_mask.cols > 0) ? static_cast<float>(img.cols) / pin_mask.cols : 0.0f;
const float pin_scale_y = (pin_mask.rows > 0) ? static_cast<float>(img.rows) / pin_mask.rows : 0.0f; const float pin_scale_y = (pin_mask.rows > 0) ? static_cast<float>(img.rows) / pin_mask.rows : 0.0f;
// 将轮廓点从 mask 坐标系 映射回 tplImg 坐标系 // 将轮廓点从 mask 坐标系 映射回 tplImg 坐标系
m_curPinContours.clear();
m_curPinContours.reserve(pin_contours.size()); m_curPinContours.reserve(pin_contours.size());
for (size_t i = 0; i < pin_contours.size(); i++) 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); m_curPinContours.push_back(mapped);
} }
// 根据模板Pin和当前Pin轮廓进行对比判断当前有无缺失和偏移
{
// 计算轮廓中心点(外接矩形中心)
auto getCenter = [](const std::vector<cv::Point> &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<int>(m_tplPinContours.size());
const int nCur = static_cast<int>(m_curPinContours.size());
if (nTpl > 0)
{
// 1、统计模板 pin 中心与平均最小边长,用于自适应阈值
std::vector<cv::Point2f> 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<float>(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<cv::Point2f> curCenters(nCur);
for (int i = 0; i < nCur; i++)
{
curCenters[i] = getCenter(m_curPinContours[i]);
}
std::vector<bool> curUsed(nCur, false);
// 上报缺失/偏移缺陷(缺陷类型可按需调整)
auto reportPinError = [&](int pinIdx, const cv::Point2f &tplCenter, const std::string &reason)
{
QX_ERROR_INFO_ pinErr;
pinErr.Idx = static_cast<int>(m_pDetResult->pQx_ErrorList->size());
cv::Rect r = cv::boundingRect(m_tplPinContours[pinIdx]);
pinErr.roi = r;
pinErr.area = static_cast<int>(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<int>(tplCenter.x), static_cast<int>(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<double>(dx) * dx + static_cast<double>(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) if (m_Edge_DetConfig.bSaveResultImg)
{ {

Loading…
Cancel
Save