|
|
|
@ -180,9 +180,6 @@ static int GetClsTypeByLabel(int nLabel)
|
|
|
|
case 6:
|
|
|
|
case 6:
|
|
|
|
cls_num = AI_CLass_QX_NAME_C_BianXing;
|
|
|
|
cls_num = AI_CLass_QX_NAME_C_BianXing;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
|
|
|
|
cls_num = AI_CLass_QX_NAME_Panel;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -547,101 +544,46 @@ cv::Scalar ImgCheckAnalysisy::calc_blob_info_withstats(cv::Mat &img, const cv::M
|
|
|
|
return cv::Scalar(worb, energy, hj);
|
|
|
|
return cv::Scalar(worb, energy, hj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算仿射矩阵(不做图像 warp):
|
|
|
|
// 仿射变换到模板坐标系:把当前基准矩形(内定位/外定位)的顶点映射到模板对应基准矩形上
|
|
|
|
// 基准点用当前基准矩形(内定位优先/外定位回退)的三个角点 -> 模板对应基准矩形的三个角点
|
|
|
|
int ImgCheckAnalysisy::Adapt_Config(RotatedRect tplRefRect, RotatedRect curRefRect, Mat& det_img){
|
|
|
|
// m_affine_cur2tpl: 当前图坐标 -> 模板图坐标
|
|
|
|
|
|
|
|
// m_affine_tpl2cur: 模板图坐标 -> 当前图坐标
|
|
|
|
|
|
|
|
int ImgCheckAnalysisy::CalAffineMats(const RotatedRect &tplRefRect, const RotatedRect &curRefRect)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_affine_cur2tpl.release();
|
|
|
|
|
|
|
|
m_affine_tpl2cur.release();
|
|
|
|
|
|
|
|
m_bAffineValid = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (tplRefRect.size.width <= 0 || tplRefRect.size.height <= 0 ||
|
|
|
|
|
|
|
|
curRefRect.size.width <= 0 || curRefRect.size.height <= 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "CalAffineMats: invalid ref rect tpl[%f %f] cur[%f %f]",
|
|
|
|
|
|
|
|
tplRefRect.size.width, tplRefRect.size.height,
|
|
|
|
|
|
|
|
curRefRect.size.width, curRefRect.size.height);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vector<cv::Point2f> cur_vertices = CheckUtil::sort_vertices(curRefRect);
|
|
|
|
vector<cv::Point2f> cur_vertices = CheckUtil::sort_vertices(curRefRect);
|
|
|
|
vector<cv::Point2f> tpl_vertices = CheckUtil::sort_vertices(tplRefRect);
|
|
|
|
vector<cv::Point2f> tpl_vertices = CheckUtil::sort_vertices(tplRefRect);
|
|
|
|
|
|
|
|
|
|
|
|
cv::Point2f cur_pts[3] = {
|
|
|
|
cv::Point2f src_pts[3] = {
|
|
|
|
cur_vertices[0], // 左上
|
|
|
|
cur_vertices[0], // 对应左上
|
|
|
|
cur_vertices[1], // 右上
|
|
|
|
cur_vertices[1], // 对应右上
|
|
|
|
cur_vertices[2] // 左下
|
|
|
|
cur_vertices[2] // 对应左下
|
|
|
|
};
|
|
|
|
};
|
|
|
|
cv::Point2f tpl_pts[3] = {
|
|
|
|
cv::Point2f dst_pts[3] = {
|
|
|
|
tpl_vertices[0], // 左上
|
|
|
|
tpl_vertices[0], // 左上
|
|
|
|
tpl_vertices[1], // 右上
|
|
|
|
tpl_vertices[1], // 右上
|
|
|
|
tpl_vertices[2] // 左下
|
|
|
|
tpl_vertices[2] // 左下
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
cv::Mat affine_mat = cv::getAffineTransform(src_pts, dst_pts);
|
|
|
|
|
|
|
|
|
|
|
|
m_affine_cur2tpl = cv::getAffineTransform(cur_pts, tpl_pts);
|
|
|
|
// 仿射变换
|
|
|
|
m_affine_tpl2cur = cv::getAffineTransform(tpl_pts, cur_pts);
|
|
|
|
cv::warpAffine(det_img, det_img, affine_mat, det_img.size());
|
|
|
|
m_bAffineValid = (!m_affine_cur2tpl.empty() && !m_affine_tpl2cur.empty());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!m_bAffineValid)
|
|
|
|
// 将m_outer_rroi和m_inner_rroi变换到模板图像坐标系下
|
|
|
|
|
|
|
|
auto transformRotatedRect = [&affine_mat](cv::RotatedRect &rrect)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "CalAffineMats: getAffineTransform failed");
|
|
|
|
const double *M = affine_mat.ptr<double>(0);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 仅用于日志: 分解当前->模板的旋转/缩放, 便于排查
|
|
|
|
cv::Point2f pts[4];
|
|
|
|
const double *M = m_affine_cur2tpl.ptr<double>(0);
|
|
|
|
rrect.points(pts);
|
|
|
|
double fScaleX = std::sqrt(M[0] * M[0] + M[3] * M[3]);
|
|
|
|
|
|
|
|
double fScaleY = std::sqrt(M[1] * M[1] + M[4] * M[4]);
|
|
|
|
|
|
|
|
double fAngle = std::atan2(M[3], M[0]) * 180.0 / CV_PI;
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "CalAffineMats",
|
|
|
|
|
|
|
|
"scaleX %.4f scaleY %.4f angle %.2f deg t(%.2f %.2f)",
|
|
|
|
|
|
|
|
fScaleX, fScaleY, fAngle, M[2], M[5]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 把模板 Pin 轮廓反变换到当前检测图(detImg)坐标系:
|
|
|
|
std::vector<cv::Point2f> transformed_pts(4);
|
|
|
|
// m_tplPinContours 在 pinImg(模板外框裁剪图)坐标系
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
// -> 加模板外框原点得到模板图坐标
|
|
|
|
|
|
|
|
// -> m_affine_tpl2cur 得到当前图坐标
|
|
|
|
|
|
|
|
// -> 减 m_outer_roi 原点得到 detImg 坐标
|
|
|
|
|
|
|
|
int ImgCheckAnalysisy::TransformTplPinContoursToCur()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_tplPinContoursCur.clear();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (m_tplPinContours.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_bAffineValid || m_affine_tpl2cur.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error",
|
|
|
|
float x = static_cast<float>(M[0] * pts[i].x + M[1] * pts[i].y + M[2]);
|
|
|
|
"TransformTplPinContoursToCur: affine invalid, template pin contours skipped");
|
|
|
|
float y = static_cast<float>(M[3] * pts[i].x + M[4] * pts[i].y + M[5]);
|
|
|
|
return 1;
|
|
|
|
transformed_pts[i] = cv::Point2f(x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rrect = cv::minAreaRect(transformed_pts);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const cv::Rect tplBound = m_tplOuterRect.boundingRect();
|
|
|
|
transformRotatedRect(m_outer_rroi);
|
|
|
|
const double *M = m_affine_tpl2cur.ptr<double>(0);
|
|
|
|
transformRotatedRect(m_inner_rroi);
|
|
|
|
|
|
|
|
|
|
|
|
m_tplPinContoursCur.reserve(m_tplPinContours.size());
|
|
|
|
|
|
|
|
for (const auto &contour : m_tplPinContours)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::vector<cv::Point> mapped;
|
|
|
|
|
|
|
|
mapped.reserve(contour.size());
|
|
|
|
|
|
|
|
for (const auto &pt : contour)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// pinImg 坐标 -> 模板图坐标
|
|
|
|
|
|
|
|
const double tplX = static_cast<double>(pt.x) + tplBound.x;
|
|
|
|
|
|
|
|
const double tplY = static_cast<double>(pt.y) + tplBound.y;
|
|
|
|
|
|
|
|
// 模板图坐标 -> 当前图坐标
|
|
|
|
|
|
|
|
const double curX = M[0] * tplX + M[1] * tplY + M[2];
|
|
|
|
|
|
|
|
const double curY = M[3] * tplX + M[4] * tplY + M[5];
|
|
|
|
|
|
|
|
// 当前图坐标 -> detImg 坐标
|
|
|
|
|
|
|
|
mapped.emplace_back(cvRound(curX - m_outer_roi.x), cvRound(curY - m_outer_roi.y));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
m_tplPinContoursCur.push_back(std::move(mapped));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -661,8 +603,7 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
m_pdetlog->bPrintStr = true;
|
|
|
|
m_pdetlog->bPrintStr = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// m_pdetlog->bPrintStr = true;
|
|
|
|
// m_pdetlog->bPrintStr = true;
|
|
|
|
m_fImgage_Scale_X = m_pBasicConfig->fImage_Scale_x;
|
|
|
|
|
|
|
|
m_fImgage_Scale_Y = m_pBasicConfig->fImage_Scale_y;
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "1、basic Info", "---------------------------1、basic Info---------------------------------");
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "1、basic Info", "---------------------------1、basic Info---------------------------------");
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Version", "%s", GetVersion().c_str());
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Version", "%s", GetVersion().c_str());
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Updateconfig", "%d", m_bupdateconfig);
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Updateconfig", "%d", m_bupdateconfig);
|
|
|
|
@ -707,12 +648,6 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
float aouter = CheckUtil::getLongAxisAngle(m_outer_rroi);
|
|
|
|
float aouter = CheckUtil::getLongAxisAngle(m_outer_rroi);
|
|
|
|
float ainner = CheckUtil::getLongAxisAngle(m_inner_rroi);
|
|
|
|
float ainner = CheckUtil::getLongAxisAngle(m_inner_rroi);
|
|
|
|
|
|
|
|
|
|
|
|
// 计算内边缘宽高与标准差值
|
|
|
|
|
|
|
|
float dinnerLen = std::abs(m_inner_rroi.size.width - m_pbaseCheckFunction->ALign_Config.len) * m_fImgage_Scale_X;
|
|
|
|
|
|
|
|
float dinnerWidth = std::abs(m_inner_rroi.size.height - m_pbaseCheckFunction->ALign_Config.width) * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool bSizeerr = (dinnerLen > m_pbaseCheckFunction->ALign_Config.len_diff || dinnerWidth > m_pbaseCheckFunction->ALign_Config.width_diff);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 度
|
|
|
|
|
|
|
|
|
|
|
|
@ -730,46 +665,46 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 使用 ALign_Config.x_offset / y_offset (mm) 卡控内外边缘中心偏移(mm 转像素)
|
|
|
|
// 2. 使用 ALign_Config.x_offset / y_offset (mm) 卡控内外边缘中心偏移(mm 转像素)
|
|
|
|
float offX_mm = 0.0f;
|
|
|
|
float offX_px = 0.0f;
|
|
|
|
float offY_mm = 0.0f;
|
|
|
|
float offY_px = 0.0f;
|
|
|
|
if (m_pbaseCheckFunction != NULL)
|
|
|
|
if (m_pbaseCheckFunction != NULL)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (m_fImgage_Scale_X > 0.0f)
|
|
|
|
if (m_fImgage_Scale_X > 0.0f)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
offX_mm = m_pbaseCheckFunction->ALign_Config.x_offset;
|
|
|
|
offX_px = m_pbaseCheckFunction->ALign_Config.x_offset / m_fImgage_Scale_X;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_fImgage_Scale_Y > 0.0f)
|
|
|
|
if (m_fImgage_Scale_Y > 0.0f)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
offY_mm = m_pbaseCheckFunction->ALign_Config.y_offset;
|
|
|
|
offY_px = m_pbaseCheckFunction->ALign_Config.y_offset / m_fImgage_Scale_Y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 内外边缘中心坐标偏移(像素)
|
|
|
|
// 3. 内外边缘中心坐标偏移(像素)
|
|
|
|
float dx_mm = std::fabs(m_outer_rroi.center.x - m_inner_rroi.center.x) * m_fImgage_Scale_X;
|
|
|
|
float dx_px = std::fabs(m_outer_rroi.center.x - m_inner_rroi.center.x);
|
|
|
|
float dy_mm = std::fabs(m_outer_rroi.center.y - m_inner_rroi.center.y) * m_fImgage_Scale_Y;
|
|
|
|
float dy_px = std::fabs(m_outer_rroi.center.y - m_inner_rroi.center.y);
|
|
|
|
|
|
|
|
|
|
|
|
bool bAngleErr = diff_align > angleTh;
|
|
|
|
bool bAngleErr = diff_align > angleTh;
|
|
|
|
bool bOffsetXErr = (offX_mm > 0.0f) && (dx_mm > offX_mm);
|
|
|
|
bool bOffsetXErr = (offX_px > 0.0f) && (dx_px > offX_px);
|
|
|
|
bool bOffsetYErr = (offY_mm > 0.0f) && (dy_mm > offY_mm);
|
|
|
|
bool bOffsetYErr = (offY_px > 0.0f) && (dy_px > offY_px);
|
|
|
|
bool bOffsetErr = bOffsetXErr || bOffsetYErr;
|
|
|
|
bool bOffsetErr = bOffsetXErr || bOffsetYErr;
|
|
|
|
|
|
|
|
|
|
|
|
// 角度超差或中心偏移超差 -> 添加到缺陷列表
|
|
|
|
// 角度超差或中心偏移超差 -> 添加到缺陷列表
|
|
|
|
if (bAngleErr || bOffsetErr || bSizeerr)
|
|
|
|
if (bAngleErr || bOffsetErr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
QX_ERROR_INFO_ alignErr;
|
|
|
|
QX_ERROR_INFO_ alignErr;
|
|
|
|
alignErr.Idx = static_cast<int>(m_pDetResult->pQx_ErrorList->size());
|
|
|
|
alignErr.Idx = static_cast<int>(m_pDetResult->pQx_ErrorList->size());
|
|
|
|
|
|
|
|
|
|
|
|
// roi 用整张检测图区域(detImg 坐标系); detImg 现在取当前外定位包围框, 尺寸与之一致
|
|
|
|
// roi 用整张检测图区域(detImg 坐标系)
|
|
|
|
cv::Rect curBound = m_outer_rroi.boundingRect();
|
|
|
|
cv::Rect tplRect = m_tplOuterRect.boundingRect();
|
|
|
|
alignErr.roi = cv::Rect(0, 0, curBound.width, curBound.height);
|
|
|
|
alignErr.roi = cv::Rect(0, 0, tplRect.width, tplRect.height);
|
|
|
|
alignErr.area = alignErr.roi.width * alignErr.roi.height;
|
|
|
|
alignErr.area = alignErr.roi.width * alignErr.roi.height;
|
|
|
|
alignErr.JudgArea = alignErr.area * m_fImgage_Scale_X * m_fImgage_Scale_Y;
|
|
|
|
alignErr.JudgArea = alignErr.area * m_fImgage_Scale_X * m_fImgage_Scale_Y;
|
|
|
|
alignErr.flen = curBound.width * m_fImgage_Scale_X;
|
|
|
|
alignErr.flen = tplRect.width * m_fImgage_Scale_X;
|
|
|
|
alignErr.fbreadth = curBound.height * m_fImgage_Scale_Y;
|
|
|
|
alignErr.fbreadth = tplRect.height * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
|
|
|
|
// 缺陷列表中原 other(其他)已移除;内外边缘对齐超差暂按 C型卡口异常 上报(待确认)
|
|
|
|
// 缺陷列表中原 other(其他)已移除;内外边缘对齐超差暂按 C型卡口异常 上报(待确认)
|
|
|
|
alignErr.nconfig_qx_type = CONFIG_QX_NAME_HunLiao;
|
|
|
|
alignErr.nconfig_qx_type = CONFIG_QX_NAME_C_BianXing;
|
|
|
|
alignErr.qx_name = CONFIG_QX_NAME_Names[CONFIG_QX_NAME_HunLiao];
|
|
|
|
alignErr.qx_name = CONFIG_QX_NAME_Names[CONFIG_QX_NAME_C_BianXing];
|
|
|
|
alignErr.grayDis = diff_align; // 记录内外边缘夹角(度)
|
|
|
|
alignErr.grayDis = diff_align; // 记录内外边缘夹角(度)
|
|
|
|
alignErr.detRegionidxList.push_back(0);
|
|
|
|
alignErr.detRegionidxList.push_back(0);
|
|
|
|
|
|
|
|
|
|
|
|
@ -783,18 +718,13 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bOffsetXErr)
|
|
|
|
if (bOffsetXErr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align Check", "center offset dx %.1f mm > %.1f mm",
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align Check", "center offset dx %.1f px > %.1f px",
|
|
|
|
dx_mm, offX_mm);
|
|
|
|
dx_px, offX_px);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bOffsetYErr)
|
|
|
|
if (bOffsetYErr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align Check", "center offset dy %.1f mm > %.1f mm",
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align Check", "center offset dy %.1f px > %.1f px",
|
|
|
|
dy_mm, offY_mm);
|
|
|
|
dy_px, offY_px);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bSizeerr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align Check", "inner size diff len %.1f mm, width %.1f mm > threshold",
|
|
|
|
|
|
|
|
dinnerLen, dinnerWidth);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -821,44 +751,21 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#else
|
|
|
|
// 检测PIN偏移/缺失对位置基准最敏感:PIN 位置的真实基准是产品内腔(内定位),
|
|
|
|
// 检测PIN偏移/缺失对位置基准最敏感:PIN 位置的真实基准是产品内腔(内定位),
|
|
|
|
// 因此优先用内定位计算仿射(当前内边缘 -> 模板内边缘);模板/当前内定位无效时回退外定位。
|
|
|
|
// 因此优先用内定位做仿射变换(当前内边缘 -> 模板内边缘);
|
|
|
|
// 注意: 这里不再对原图做 warpAffine, 只计算矩阵, 并把模板 Pin 轮廓反变换到当前图坐标系。
|
|
|
|
// 模板内定位/当前内定位缺失或无效时,回退到外定位(与旧行为一致)
|
|
|
|
bool bUseInnerAlign = (m_tplInnerRect.size.width > 0 && m_tplInnerRect.size.height > 0 &&
|
|
|
|
bool bUseInnerAlign = (m_tplInnerRect.size.width > 0 && m_tplInnerRect.size.height > 0 &&
|
|
|
|
m_inner_rroi.size.width > 0 && m_inner_rroi.size.height > 0);
|
|
|
|
m_inner_rroi.size.width > 0 && m_inner_rroi.size.height > 0);
|
|
|
|
if (bUseInnerAlign)
|
|
|
|
if (bUseInnerAlign)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect",
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "Affine align basis: Inner(inner_rroi -> tplInnerRect)");
|
|
|
|
"Affine align basis: Inner(inner_rroi -> tplInnerRect), template pins warp only, image NOT warped");
|
|
|
|
Adapt_Config(m_tplInnerRect, m_inner_rroi, m_CheckResult_shareP->in_shareImage->img);
|
|
|
|
CalAffineMats(m_tplInnerRect, m_inner_rroi);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect",
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "Affine align basis: Outer(outer_rroi -> tplOuterRect)");
|
|
|
|
"Affine align basis: Outer(outer_rroi -> tplOuterRect), template pins warp only, image NOT warped");
|
|
|
|
Adapt_Config(m_tplOuterRect, m_outer_rroi, m_CheckResult_shareP->in_shareImage->img);
|
|
|
|
CalAffineMats(m_tplOuterRect, m_outer_rroi);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 后续所有检测/裁剪都在当前图坐标系: 裁剪框取当前外定位的包围框(原图不 warp)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Rect curOuterBound = m_outer_rroi.boundingRect();
|
|
|
|
|
|
|
|
cv::Rect curImgBound(0, 0, m_CheckResult_shareP->in_shareImage->img.cols,
|
|
|
|
|
|
|
|
m_CheckResult_shareP->in_shareImage->img.rows);
|
|
|
|
|
|
|
|
m_outer_roi = curOuterBound & curImgBound;
|
|
|
|
|
|
|
|
if (m_outer_roi.width <= 0 || m_outer_roi.height <= 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "Affine mode: current outer roi invalid [%d %d %d %d]",
|
|
|
|
|
|
|
|
curOuterBound.x, curOuterBound.y, curOuterBound.width, curOuterBound.height);
|
|
|
|
|
|
|
|
m_nErrorCode = CHECK_ERROR_Config_cutRoi;
|
|
|
|
|
|
|
|
m_nCheckResultErrorCode = m_nErrorCode;
|
|
|
|
|
|
|
|
return m_nErrorCode;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_outer_roi = m_tplOuterRect.boundingRect();
|
|
|
|
// 模板 Pin(pinImg 坐标) -> 模板图坐标 -> 当前图坐标 -> detImg 坐标
|
|
|
|
|
|
|
|
TransformTplPinContoursToCur();
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "2.1、pre detect",
|
|
|
|
|
|
|
|
"tpl pin contours -> current frame: %ld (affineValid %d)",
|
|
|
|
|
|
|
|
static_cast<long>(m_tplPinContoursCur.size()), static_cast<int>(m_bAffineValid));
|
|
|
|
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
m_Crop_Roi_paramImg = m_outer_roi;
|
|
|
|
m_Crop_Roi_paramImg = m_outer_roi;
|
|
|
|
m_pImageAllResult->pDetResult->CutRoi = m_outer_roi;
|
|
|
|
m_pImageAllResult->pDetResult->CutRoi = m_outer_roi;
|
|
|
|
@ -1649,7 +1556,6 @@ int ImgCheckAnalysisy::ConfigCheck(cv::Mat img)
|
|
|
|
|
|
|
|
|
|
|
|
int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg)
|
|
|
|
int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::string cur_time_s = CheckUtil::getCurTimeHMS();
|
|
|
|
|
|
|
|
// 依次使用多个检测模型,各模型输出 mask 取并集作为最终 outmask;
|
|
|
|
// 依次使用多个检测模型,各模型输出 mask 取并集作为最终 outmask;
|
|
|
|
// Defect_QueXi 模型是否参与合并由基础参数 Merge_QueXi 开关控制
|
|
|
|
// Defect_QueXi 模型是否参与合并由基础参数 Merge_QueXi 开关控制
|
|
|
|
std::vector<std::shared_ptr<AIModel_Base>> pAIDetList;
|
|
|
|
std::vector<std::shared_ptr<AIModel_Base>> pAIDetList;
|
|
|
|
@ -1779,10 +1685,12 @@ int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg)
|
|
|
|
// 目录结构:/home/aidlux/BOE/ZB_SOCKET/AI_Detect/<模型名称>/<产品ID>/
|
|
|
|
// 目录结构:/home/aidlux/BOE/ZB_SOCKET/AI_Detect/<模型名称>/<产品ID>/
|
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveAIDetImg && !modelMaskFull.empty())
|
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveAIDetImg && !modelMaskFull.empty())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + DetImgInfo_shareP->strImgProductID + "/";
|
|
|
|
std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/KK/" + strModelName + "/" + DetImgInfo_shareP->strImgProductID + "/";
|
|
|
|
CheckUtil::CreateDir(strSaveDir);
|
|
|
|
CheckUtil::CreateDir(strSaveDir);
|
|
|
|
std::string strSavePath_out = strSaveDir + cur_time_s + "_" + m_strCurDetChannel + "_" + strModelName + "_out.png";
|
|
|
|
std::string strSavePath_in = strSaveDir + m_strCurDetChannel + "_in.png";
|
|
|
|
CheckUtil::SaveImgAsync(strSavePath_out, modelMaskFull);
|
|
|
|
std::string strSavePath_out = strSaveDir + m_strCurDetChannel + "_out.png";
|
|
|
|
|
|
|
|
cv::imwrite(strSavePath_in, detInputImg);
|
|
|
|
|
|
|
|
cv::imwrite(strSavePath_out, modelMaskFull);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4、mask 结果放入队列,用于 AIMaskDet 加速 blob 计算(整图 ROI)
|
|
|
|
// 4、mask 结果放入队列,用于 AIMaskDet 加速 blob 计算(整图 ROI)
|
|
|
|
@ -1837,13 +1745,6 @@ int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg)
|
|
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "AI_Detect End");
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "AI_Detect End");
|
|
|
|
|
|
|
|
|
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveAIDetImg )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + DetImgInfo_shareP->strImgProductID + "/";
|
|
|
|
|
|
|
|
CheckUtil::CreateDir(strSaveDir);
|
|
|
|
|
|
|
|
std::string strSavePath_in = strSaveDir + cur_time_s + "_" + m_strCurDetChannel + "_in.png";
|
|
|
|
|
|
|
|
CheckUtil::SaveImgAsync(strSavePath_in, m_CheckResult_shareP->cutSrcimg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 临时存图
|
|
|
|
// 临时存图
|
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -1930,8 +1831,8 @@ int ImgCheckAnalysisy::AI_QX_Class_Thread()
|
|
|
|
// 分类 label 映射:优先读映射配置文件(KK_cls.txt),读不到用默认顺序
|
|
|
|
// 分类 label 映射:优先读映射配置文件(KK_cls.txt),读不到用默认顺序
|
|
|
|
int cls_num = GetClsTypeByLabel(temClass);
|
|
|
|
int cls_num = GetClsTypeByLabel(temClass);
|
|
|
|
|
|
|
|
|
|
|
|
std::string strclassName = AI_CLass_QX_NAME_Names[cls_num];
|
|
|
|
// std::string strclassName = AI_CLass_QX_NAME_Names[cls_num];
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, "AI Class num", "%d = %s %f\n", cls_num, strclassName.c_str(), result->cls_score);
|
|
|
|
// printf("AI Class num %d = %s %f\n", cls_num, strclassName.c_str(), result->cls_score);
|
|
|
|
|
|
|
|
|
|
|
|
// 分类存图 开启。
|
|
|
|
// 分类存图 开启。
|
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveClsImg)
|
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveClsImg)
|
|
|
|
@ -2141,7 +2042,7 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
|
|
|
|
cv::Point(cvRound(mark.x), cvRound(mark.y + 6.0f))});
|
|
|
|
cv::Point(cvRound(mark.x), cvRound(mark.y + 6.0f))});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#else
|
|
|
|
const std::vector<std::vector<cv::Point>> &tplDrawPinContours = m_tplPinContoursCur;
|
|
|
|
const std::vector<std::vector<cv::Point>> &tplDrawPinContours = m_tplPinContours;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
for (const auto &contour : tplDrawPinContours)
|
|
|
|
for (const auto &contour : tplDrawPinContours)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -2189,7 +2090,7 @@ int ImgCheckAnalysisy::AIClassTypeToConfigType(int nAIQXType)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
// 无对应缺陷类型时返回 CONFIG_QX_NAME_count(缺陷列表已移除 other)
|
|
|
|
// 无对应缺陷类型时返回 CONFIG_QX_NAME_count(缺陷列表已移除 other)
|
|
|
|
int resultError_type = AI_CLass_QX_NAME_Panel;
|
|
|
|
int resultError_type = CONFIG_QX_NAME_count;
|
|
|
|
switch (nAIQXType)
|
|
|
|
switch (nAIQXType)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
case AI_CLass_QX_NAME_PIN_PoSun:
|
|
|
|
case AI_CLass_QX_NAME_PIN_PoSun:
|
|
|
|
@ -2213,9 +2114,6 @@ int ImgCheckAnalysisy::AIClassTypeToConfigType(int nAIQXType)
|
|
|
|
case AI_CLass_QX_NAME_C_BianXing:
|
|
|
|
case AI_CLass_QX_NAME_C_BianXing:
|
|
|
|
resultError_type = CONFIG_QX_NAME_C_BianXing;
|
|
|
|
resultError_type = CONFIG_QX_NAME_C_BianXing;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case AI_CLass_QX_NAME_Panel:
|
|
|
|
|
|
|
|
resultError_type = CONFIG_QX_NAME_Panel;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -2288,18 +2186,18 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
|
|
|
|
return cv::Point2f(r.x + r.width * 0.5f, r.y + r.height * 0.5f);
|
|
|
|
return cv::Point2f(r.x + r.width * 0.5f, r.y + r.height * 0.5f);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const int nTpl = static_cast<int>(m_tplPinContoursCur.size());
|
|
|
|
const int nTpl = static_cast<int>(m_tplPinContours.size());
|
|
|
|
const int nCur = static_cast<int>(m_curPinContours.size());
|
|
|
|
const int nCur = static_cast<int>(m_curPinContours.size());
|
|
|
|
|
|
|
|
|
|
|
|
if (nTpl > 0)
|
|
|
|
if (nTpl > 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 1、统计模板 pin 中心与外包矩形(已反变换到当前检测图坐标系)
|
|
|
|
// 1、统计模板 pin 中心与外包矩形
|
|
|
|
std::vector<cv::Point2f> tplCenters(nTpl);
|
|
|
|
std::vector<cv::Point2f> tplCenters(nTpl);
|
|
|
|
std::vector<cv::Rect> tplRects(nTpl);
|
|
|
|
std::vector<cv::Rect> tplRects(nTpl);
|
|
|
|
for (int i = 0; i < nTpl; i++)
|
|
|
|
for (int i = 0; i < nTpl; i++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
tplCenters[i] = getCenter(m_tplPinContoursCur[i]);
|
|
|
|
tplCenters[i] = getCenter(m_tplPinContours[i]);
|
|
|
|
tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]);
|
|
|
|
tplRects[i] = cv::boundingRect(m_tplPinContours[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2、当前 pin 中心与外包矩形
|
|
|
|
// 2、当前 pin 中心与外包矩形
|
|
|
|
@ -2312,13 +2210,19 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::vector<bool> curUsed(nCur, false);
|
|
|
|
std::vector<bool> curUsed(nCur, false);
|
|
|
|
|
|
|
|
|
|
|
|
// 3、偏移阈值直接使用基础参数 pin_x_offset / pin_y_offset (mm),与换算为 mm 的偏移量直接比较
|
|
|
|
// 3、偏移阈值由基础参数 pin_x_offset / pin_y_offset (mm) 换算为像素
|
|
|
|
float offsetThX = 0.0f; // mm
|
|
|
|
float offsetThX = 0.0f;
|
|
|
|
float offsetThY = 0.0f; // mm
|
|
|
|
float offsetThY = 0.0f;
|
|
|
|
if (m_pbaseCheckFunction != NULL)
|
|
|
|
if (m_pbaseCheckFunction != NULL)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
offsetThX = m_pbaseCheckFunction->pinDet.pin_x_offset;
|
|
|
|
if (m_fImgage_Scale_X > 0.0f)
|
|
|
|
offsetThY = m_pbaseCheckFunction->pinDet.pin_y_offset;
|
|
|
|
{
|
|
|
|
|
|
|
|
offsetThX = m_pbaseCheckFunction->pinDet.pin_x_offset / m_fImgage_Scale_X;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_fImgage_Scale_Y > 0.0f)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
offsetThY = m_pbaseCheckFunction->pinDet.pin_y_offset / m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 上报缺失/偏移缺陷(缺陷类型可按需调整)
|
|
|
|
// 上报缺失/偏移缺陷(缺陷类型可按需调整)
|
|
|
|
@ -2326,9 +2230,9 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
QX_ERROR_INFO_ pinErr;
|
|
|
|
QX_ERROR_INFO_ pinErr;
|
|
|
|
pinErr.Idx = static_cast<int>(m_pDetResult->pQx_ErrorList->size());
|
|
|
|
pinErr.Idx = static_cast<int>(m_pDetResult->pQx_ErrorList->size());
|
|
|
|
cv::Rect r = cv::boundingRect(m_tplPinContoursCur[pinIdx]);
|
|
|
|
cv::Rect r = cv::boundingRect(m_tplPinContours[pinIdx]);
|
|
|
|
pinErr.roi = r;
|
|
|
|
pinErr.roi = r;
|
|
|
|
pinErr.area = static_cast<int>(cv::contourArea(m_tplPinContoursCur[pinIdx]));
|
|
|
|
pinErr.area = static_cast<int>(cv::contourArea(m_tplPinContours[pinIdx]));
|
|
|
|
pinErr.JudgArea = pinErr.area * m_fImgage_Scale_X * m_fImgage_Scale_Y;
|
|
|
|
pinErr.JudgArea = pinErr.area * m_fImgage_Scale_X * m_fImgage_Scale_Y;
|
|
|
|
int longSide = (r.width > r.height) ? r.width : r.height;
|
|
|
|
int longSide = (r.width > r.height) ? r.width : r.height;
|
|
|
|
int shortSide = (r.width < r.height) ? r.width : r.height;
|
|
|
|
int shortSide = (r.width < r.height) ? r.width : r.height;
|
|
|
|
@ -2387,21 +2291,19 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
|
|
|
|
|
|
|
|
|
|
|
|
curUsed[bestIdx] = true;
|
|
|
|
curUsed[bestIdx] = true;
|
|
|
|
|
|
|
|
|
|
|
|
// x 或 y 方向偏移超过配置阈值 -> 偏移;偏移量按成像精度换算为 mm (mm = px * 成像精度)
|
|
|
|
// x 或 y 方向偏移超过配置阈值 -> 偏移
|
|
|
|
float dx_mm = (tplCenters[i].x - curCenters[bestIdx].x) * m_fImgage_Scale_X;
|
|
|
|
float dx = tplCenters[i].x - curCenters[bestIdx].x;
|
|
|
|
float dy_mm = (tplCenters[i].y - curCenters[bestIdx].y) * m_fImgage_Scale_Y;
|
|
|
|
float dy = tplCenters[i].y - curCenters[bestIdx].y;
|
|
|
|
float absDx = std::fabs(dx_mm);
|
|
|
|
float absDx = std::fabs(dx);
|
|
|
|
float absDy = std::fabs(dy_mm);
|
|
|
|
float absDy = std::fabs(dy);
|
|
|
|
bool bOffset = (absDx > offsetThX || absDy > offsetThY);
|
|
|
|
if (absDx > offsetThX || absDy > offsetThY)
|
|
|
|
if (bOffset)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
nOffset++;
|
|
|
|
nOffset++;
|
|
|
|
char buf[64];
|
|
|
|
char buf[64];
|
|
|
|
snprintf(buf, sizeof(buf), "offset dx %.2f dy %.2f mm", absDx, absDy);
|
|
|
|
snprintf(buf, sizeof(buf), "offset dx %.1f dy %.1f", absDx, absDy);
|
|
|
|
reportPinError(i, tplCenters[i], buf, CONFIG_QX_NAME_PIN_PianYi);
|
|
|
|
reportPinError(i, tplCenters[i], buf, CONFIG_QX_NAME_PIN_PianYi);
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "pin %d OFFSET dx %.1f dy %.1f", i, absDx, absDy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 每个pin脚都打印dx/dy(mm),方便调整 pin_x_offset / pin_y_offset 参数
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "pin %d %s dx %.2f dy %.2f mm", i, bOffset ? "OFFSET" : "OK", absDx, absDy);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "tpl %d cur %d miss %d offset %d", nTpl, nCur, nMiss, nOffset);
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "tpl %d cur %d miss %d offset %d", nTpl, nCur, nMiss, nOffset);
|
|
|
|
@ -2442,7 +2344,7 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
|
|
|
|
CheckUtil::GetDrawColor(showimg, cv::Scalar(0, 255, 255)), cv::MARKER_CROSS, 16, 1);
|
|
|
|
CheckUtil::GetDrawColor(showimg, cv::Scalar(0, 255, 255)), cv::MARKER_CROSS, 16, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#else
|
|
|
|
cv::drawContours(showimg, m_tplPinContoursCur, -1, CheckUtil::GetDrawColor(showimg, cv::Scalar(0, 255, 0)), 1);
|
|
|
|
cv::drawContours(showimg, m_tplPinContours, -1, CheckUtil::GetDrawColor(showimg, cv::Scalar(0, 255, 0)), 1);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
imwrite("pin_contours.png", showimg);
|
|
|
|
imwrite("pin_contours.png", showimg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -2719,8 +2621,8 @@ int ImgCheckAnalysisy::Pin_Direct_Match()
|
|
|
|
// 新方案(开关 PIN_USE_DIRECT_MATCH = 1): 不做模板对比, 只针对检测Pin分析
|
|
|
|
// 新方案(开关 PIN_USE_DIRECT_MATCH = 1): 不做模板对比, 只针对检测Pin分析
|
|
|
|
// 1) 方向扫描(0~180°, 步长1°): 取 "两排Pin沿法线投影组内方差之和" 最小的角度作为Pin排方向(对图像旋转不敏感),
|
|
|
|
// 1) 方向扫描(0~180°, 步长1°): 取 "两排Pin沿法线投影组内方差之和" 最小的角度作为Pin排方向(对图像旋转不敏感),
|
|
|
|
// 再沿该法线投影, 在最大间隙处把Pin分成两排(两排插口; Pin不足4个则按一排处理);
|
|
|
|
// 再沿该法线投影, 在最大间隙处把Pin分成两排(两排插口; Pin不足4个则按一排处理);
|
|
|
|
// 2) 每排做最小二乘直线拟合(cv::fitLine): pin中心到直线(沿法线方向)距离按成像精度换算为 mm, 超过 pin_y_offset(mm) 判 偏移;
|
|
|
|
// 2) 每排做最小二乘直线拟合(cv::fitLine): pin中心到直线(沿法线方向)距离超过 pin_y_offset(换算像素) 判 偏移;
|
|
|
|
// 3) 每排内沿排方向排序: 相邻两pin中心连线距离按成像精度换算为 mm, 超过 pin_x_offset(mm) 判 两pin中间缺失一个pin。
|
|
|
|
// 3) 每排内沿排方向排序: 相邻两pin中心连线距离超过 pin_x_offset(换算像素) 判 两pin中间缺失一个pin。
|
|
|
|
int ImgCheckAnalysisy::Pin_Direct_Check()
|
|
|
|
int ImgCheckAnalysisy::Pin_Direct_Check()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 计算轮廓中心点(外接矩形中心)
|
|
|
|
// 计算轮廓中心点(外接矩形中心)
|
|
|
|
@ -2752,13 +2654,19 @@ int ImgCheckAnalysisy::Pin_Direct_Check()
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 阈值直接使用 mm 参数(判据按 mm 比较): pin_x_offset -> 相邻pin间距(缺失)阈值; pin_y_offset -> 到拟合直线距离(偏移)阈值; <=0 时不判该项
|
|
|
|
// 阈值(mm 参数换算为像素): pin_x_offset -> 相邻pin间距(缺失)阈值; pin_y_offset -> 到拟合直线距离(偏移)阈值; <=0 时不判该项
|
|
|
|
float offsetThX = 0.0f; // mm
|
|
|
|
float offsetThX = 0.0f;
|
|
|
|
float offsetThY = 0.0f; // mm
|
|
|
|
float offsetThY = 0.0f;
|
|
|
|
if (m_pbaseCheckFunction != NULL)
|
|
|
|
if (m_pbaseCheckFunction != NULL)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
offsetThX = m_pbaseCheckFunction->pinDet.pin_x_offset;
|
|
|
|
if (m_fImgage_Scale_X > 0.0f)
|
|
|
|
offsetThY = m_pbaseCheckFunction->pinDet.pin_y_offset;
|
|
|
|
{
|
|
|
|
|
|
|
|
offsetThX = m_pbaseCheckFunction->pinDet.pin_x_offset / m_fImgage_Scale_X;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_fImgage_Scale_Y > 0.0f)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
offsetThY = m_pbaseCheckFunction->pinDet.pin_y_offset / m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 沿给定方向投影排序, 在最大间隙处切分成两排(order[0..splitK] 一排, order[splitK+1..] 另一排)
|
|
|
|
// 沿给定方向投影排序, 在最大间隙处切分成两排(order[0..splitK] 一排, order[splitK+1..] 另一排)
|
|
|
|
@ -2875,11 +2783,11 @@ int ImgCheckAnalysisy::Pin_Direct_Check()
|
|
|
|
rows.push_back(order);
|
|
|
|
rows.push_back(order);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Pin Check",
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Pin Check",
|
|
|
|
"row dir %.1f deg rows %d th[gap %.2f line %.2f] mm",
|
|
|
|
"row dir %.1f deg rows %d th[gap %.1f line %.1f] px",
|
|
|
|
bestTheta * 180.0f / static_cast<float>(CV_PI),
|
|
|
|
bestTheta * 180.0f / static_cast<float>(CV_PI),
|
|
|
|
static_cast<int>(rows.size()), offsetThX, offsetThY);
|
|
|
|
static_cast<int>(rows.size()), offsetThX, offsetThY);
|
|
|
|
|
|
|
|
|
|
|
|
// 上报缺陷(roi/area 由调用处给出; grayDis 记录对应度量: 到直线距离 / 相邻间距, 单位mm)
|
|
|
|
// 上报缺陷(roi/area 由调用处给出; grayDis 记录对应度量: 到直线距离 / 相邻间距, 单位像素)
|
|
|
|
auto reportPinError = [&](const cv::Rect &roi, int area, double grayDis, int nconfig_qx_type, const std::string &detail)
|
|
|
|
auto reportPinError = [&](const cv::Rect &roi, int area, double grayDis, int nconfig_qx_type, const std::string &detail)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
QX_ERROR_INFO_ pinErr;
|
|
|
|
QX_ERROR_INFO_ pinErr;
|
|
|
|
@ -2944,28 +2852,23 @@ int ImgCheckAnalysisy::Pin_Direct_Check()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_pinFitLines.push_back(std::make_pair(lineP0 + lineDir * tMin, lineP0 + lineDir * tMax));
|
|
|
|
m_pinFitLines.push_back(std::make_pair(lineP0 + lineDir * tMin, lineP0 + lineDir * tMax));
|
|
|
|
|
|
|
|
|
|
|
|
// 2、pin中心到直线(沿法线方向)距离超过 pin_y_offset -> 偏移;距离按成像精度换算为 mm (mm = px * 成像精度)
|
|
|
|
// 2、pin中心到直线(沿法线方向)距离超过 pin_y_offset -> 偏移
|
|
|
|
if (offsetThY > 0.0f)
|
|
|
|
if (offsetThY > 0.0f)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
for (int i = 0; i < nRow; i++)
|
|
|
|
for (int i = 0; i < nRow; i++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 垂直偏移向量(px)的分量分别换算为 mm 后求模
|
|
|
|
float absDist = std::fabs((rowPts[i] - lineP0).dot(lineNor));
|
|
|
|
float distPx = (rowPts[i] - lineP0).dot(lineNor);
|
|
|
|
if (absDist > offsetThY)
|
|
|
|
float dx_mm = lineNor.x * distPx * m_fImgage_Scale_X;
|
|
|
|
|
|
|
|
float dy_mm = lineNor.y * distPx * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
float absDist = std::sqrt(dx_mm * dx_mm + dy_mm * dy_mm);
|
|
|
|
|
|
|
|
int idx = rowIdx[i];
|
|
|
|
|
|
|
|
bool bOffset = (absDist > offsetThY);
|
|
|
|
|
|
|
|
if (bOffset)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
int idx = rowIdx[i];
|
|
|
|
nOffset++;
|
|
|
|
nOffset++;
|
|
|
|
char buf[96];
|
|
|
|
char buf[96];
|
|
|
|
snprintf(buf, sizeof(buf), "pin %d offset to row line %.2f mm > %.2f mm", idx, absDist, offsetThY);
|
|
|
|
snprintf(buf, sizeof(buf), "pin %d offset to row line %.1f px > %.1f px", idx, absDist, offsetThY);
|
|
|
|
reportPinError(curRects[idx], static_cast<int>(cv::contourArea(m_curPinContours[idx])),
|
|
|
|
reportPinError(curRects[idx], static_cast<int>(cv::contourArea(m_curPinContours[idx])),
|
|
|
|
absDist, CONFIG_QX_NAME_PIN_PianYi, buf);
|
|
|
|
absDist, CONFIG_QX_NAME_PIN_PianYi, buf);
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "row %d pin %d OFFSET %.1f px > %.1f px",
|
|
|
|
|
|
|
|
static_cast<int>(r), idx, absDist, offsetThY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 每个pin脚都打印到拟合直线的距离(mm),方便调整 pin_y_offset 参数
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "row %d pin %d %s line %.2f mm", static_cast<int>(r), idx, bOffset ? "OFFSET" : "OK", absDist);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -2984,16 +2887,11 @@ int ImgCheckAnalysisy::Pin_Direct_Check()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int ia = tOrder[i].second;
|
|
|
|
int ia = tOrder[i].second;
|
|
|
|
int ib = tOrder[i + 1].second;
|
|
|
|
int ib = tOrder[i + 1].second;
|
|
|
|
// 相邻pin中心连线距离(px)的分量分别换算为 mm 后求模
|
|
|
|
float gap = static_cast<float>(cv::norm(rowPts[ib] - rowPts[ia]));
|
|
|
|
cv::Point2f dv = rowPts[ib] - rowPts[ia];
|
|
|
|
if (gap > offsetThX)
|
|
|
|
float dx_mm = dv.x * m_fImgage_Scale_X;
|
|
|
|
{
|
|
|
|
float dy_mm = dv.y * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
float gap = std::sqrt(dx_mm * dx_mm + dy_mm * dy_mm);
|
|
|
|
|
|
|
|
int idxa = rowIdx[ia];
|
|
|
|
int idxa = rowIdx[ia];
|
|
|
|
int idxb = rowIdx[ib];
|
|
|
|
int idxb = rowIdx[ib];
|
|
|
|
bool bMiss = (gap > offsetThX);
|
|
|
|
|
|
|
|
if (bMiss)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Point2f mid = (rowPts[ia] + rowPts[ib]) * 0.5f;
|
|
|
|
cv::Point2f mid = (rowPts[ia] + rowPts[ib]) * 0.5f;
|
|
|
|
m_pinMissMarks.push_back(mid);
|
|
|
|
m_pinMissMarks.push_back(mid);
|
|
|
|
|
|
|
|
|
|
|
|
@ -3007,11 +2905,11 @@ int ImgCheckAnalysisy::Pin_Direct_Check()
|
|
|
|
static_cast<int>(cv::contourArea(m_curPinContours[idxb]))) / 2;
|
|
|
|
static_cast<int>(cv::contourArea(m_curPinContours[idxb]))) / 2;
|
|
|
|
nMiss++;
|
|
|
|
nMiss++;
|
|
|
|
char buf[96];
|
|
|
|
char buf[96];
|
|
|
|
snprintf(buf, sizeof(buf), "miss between pin %d-%d gap %.2f mm > %.2f mm", idxa, idxb, gap, offsetThX);
|
|
|
|
snprintf(buf, sizeof(buf), "miss between pin %d-%d gap %.1f px > %.1f px", idxa, idxb, gap, offsetThX);
|
|
|
|
reportPinError(roiMiss, areaAvg, gap, CONFIG_QX_NAME_PIN_QueShi, buf);
|
|
|
|
reportPinError(roiMiss, areaAvg, gap, CONFIG_QX_NAME_PIN_QueShi, buf);
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "row %d pin %d-%d MISS gap %.1f px > %.1f px",
|
|
|
|
|
|
|
|
static_cast<int>(r), idxa, idxb, gap, offsetThX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 每对相邻pin都打印间距(mm),方便调整 pin_x_offset 参数
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check", "row %d pin %d-%d %s gap %.2f mm", static_cast<int>(r), idxa, idxb, bMiss ? "MISS" : "OK", gap);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|