|
|
|
@ -544,46 +544,101 @@ 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 src_pts[3] = {
|
|
|
|
cv::Point2f cur_pts[3] = {
|
|
|
|
cur_vertices[0], // 对应左上
|
|
|
|
cur_vertices[0], // 左上
|
|
|
|
cur_vertices[1], // 对应右上
|
|
|
|
cur_vertices[1], // 右上
|
|
|
|
cur_vertices[2] // 对应左下
|
|
|
|
cur_vertices[2] // 左下
|
|
|
|
};
|
|
|
|
};
|
|
|
|
cv::Point2f dst_pts[3] = {
|
|
|
|
cv::Point2f tpl_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);
|
|
|
|
cv::warpAffine(det_img, det_img, affine_mat, det_img.size());
|
|
|
|
m_affine_tpl2cur = cv::getAffineTransform(tpl_pts, cur_pts);
|
|
|
|
|
|
|
|
m_bAffineValid = (!m_affine_cur2tpl.empty() && !m_affine_tpl2cur.empty());
|
|
|
|
|
|
|
|
|
|
|
|
// 将m_outer_rroi和m_inner_rroi变换到模板图像坐标系下
|
|
|
|
if (!m_bAffineValid)
|
|
|
|
auto transformRotatedRect = [&affine_mat](cv::RotatedRect &rrect)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const double *M = affine_mat.ptr<double>(0);
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "CalAffineMats: getAffineTransform failed");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cv::Point2f pts[4];
|
|
|
|
// 仅用于日志: 分解当前->模板的旋转/缩放, 便于排查
|
|
|
|
rrect.points(pts);
|
|
|
|
const double *M = m_affine_cur2tpl.ptr<double>(0);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<cv::Point2f> transformed_pts(4);
|
|
|
|
// 把模板 Pin 轮廓反变换到当前检测图(detImg)坐标系:
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
// m_tplPinContours 在 pinImg(模板外框裁剪图)坐标系
|
|
|
|
|
|
|
|
// -> 加模板外框原点得到模板图坐标
|
|
|
|
|
|
|
|
// -> 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",
|
|
|
|
|
|
|
|
"TransformTplPinContoursToCur: affine invalid, template pin contours skipped");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const cv::Rect tplBound = m_tplOuterRect.boundingRect();
|
|
|
|
|
|
|
|
const double *M = m_affine_tpl2cur.ptr<double>(0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
float x = static_cast<float>(M[0] * pts[i].x + M[1] * pts[i].y + M[2]);
|
|
|
|
// pinImg 坐标 -> 模板图坐标
|
|
|
|
float y = static_cast<float>(M[3] * pts[i].x + M[4] * pts[i].y + M[5]);
|
|
|
|
const double tplX = static_cast<double>(pt.x) + tplBound.x;
|
|
|
|
transformed_pts[i] = cv::Point2f(x, y);
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rrect = cv::minAreaRect(transformed_pts);
|
|
|
|
m_tplPinContoursCur.push_back(std::move(mapped));
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
transformRotatedRect(m_outer_rroi);
|
|
|
|
|
|
|
|
transformRotatedRect(m_inner_rroi);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -701,13 +756,13 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
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 坐标系)
|
|
|
|
// roi 用整张检测图区域(detImg 坐标系); detImg 现在取当前外定位包围框, 尺寸与之一致
|
|
|
|
cv::Rect tplRect = m_tplOuterRect.boundingRect();
|
|
|
|
cv::Rect curBound = m_outer_rroi.boundingRect();
|
|
|
|
alignErr.roi = cv::Rect(0, 0, tplRect.width, tplRect.height);
|
|
|
|
alignErr.roi = cv::Rect(0, 0, curBound.width, curBound.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 = tplRect.width * m_fImgage_Scale_X;
|
|
|
|
alignErr.flen = curBound.width * m_fImgage_Scale_X;
|
|
|
|
alignErr.fbreadth = tplRect.height * m_fImgage_Scale_Y;
|
|
|
|
alignErr.fbreadth = curBound.height * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
|
|
|
|
// 缺陷列表中原 other(其他)已移除;内外边缘对齐超差暂按 C型卡口异常 上报(待确认)
|
|
|
|
// 缺陷列表中原 other(其他)已移除;内外边缘对齐超差暂按 C型卡口异常 上报(待确认)
|
|
|
|
alignErr.nconfig_qx_type = CONFIG_QX_NAME_HunLiao;
|
|
|
|
alignErr.nconfig_qx_type = CONFIG_QX_NAME_HunLiao;
|
|
|
|
@ -763,21 +818,44 @@ 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", "Affine align basis: Inner(inner_rroi -> tplInnerRect)");
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect",
|
|
|
|
Adapt_Config(m_tplInnerRect, m_inner_rroi, m_CheckResult_shareP->in_shareImage->img);
|
|
|
|
"Affine align basis: Inner(inner_rroi -> tplInnerRect), template pins warp only, image NOT warped");
|
|
|
|
|
|
|
|
CalAffineMats(m_tplInnerRect, m_inner_rroi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "Affine align basis: Outer(outer_rroi -> tplOuterRect)");
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect",
|
|
|
|
Adapt_Config(m_tplOuterRect, m_outer_rroi, m_CheckResult_shareP->in_shareImage->img);
|
|
|
|
"Affine align basis: Outer(outer_rroi -> tplOuterRect), template pins warp only, image NOT warped");
|
|
|
|
|
|
|
|
CalAffineMats(m_tplOuterRect, m_outer_rroi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_outer_roi = m_tplOuterRect.boundingRect();
|
|
|
|
|
|
|
|
|
|
|
|
// 后续所有检测/裁剪都在当前图坐标系: 裁剪框取当前外定位的包围框(原图不 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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 模板 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;
|
|
|
|
@ -2060,7 +2138,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_tplPinContours;
|
|
|
|
const std::vector<std::vector<cv::Point>> &tplDrawPinContours = m_tplPinContoursCur;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
for (const auto &contour : tplDrawPinContours)
|
|
|
|
for (const auto &contour : tplDrawPinContours)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -2204,18 +2282,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_tplPinContours.size());
|
|
|
|
const int nTpl = static_cast<int>(m_tplPinContoursCur.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_tplPinContours[i]);
|
|
|
|
tplCenters[i] = getCenter(m_tplPinContoursCur[i]);
|
|
|
|
tplRects[i] = cv::boundingRect(m_tplPinContours[i]);
|
|
|
|
tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2、当前 pin 中心与外包矩形
|
|
|
|
// 2、当前 pin 中心与外包矩形
|
|
|
|
@ -2242,9 +2320,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_tplPinContours[pinIdx]);
|
|
|
|
cv::Rect r = cv::boundingRect(m_tplPinContoursCur[pinIdx]);
|
|
|
|
pinErr.roi = r;
|
|
|
|
pinErr.roi = r;
|
|
|
|
pinErr.area = static_cast<int>(cv::contourArea(m_tplPinContours[pinIdx]));
|
|
|
|
pinErr.area = static_cast<int>(cv::contourArea(m_tplPinContoursCur[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;
|
|
|
|
@ -2358,7 +2436,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_tplPinContours, -1, CheckUtil::GetDrawColor(showimg, cv::Scalar(0, 255, 0)), 1);
|
|
|
|
cv::drawContours(showimg, m_tplPinContoursCur, -1, CheckUtil::GetDrawColor(showimg, cv::Scalar(0, 255, 0)), 1);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
imwrite("pin_contours.png", showimg);
|
|
|
|
imwrite("pin_contours.png", showimg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|