update 不仿射原图,对模板Pin进行仿射

dev_lsy
liusiyang 2 weeks ago
parent b7da6688ba
commit 13afe246d5

@ -218,8 +218,11 @@ private:
// 获取指定 残点(blob) 是由哪个(哪些)检测模型检出的
std::string GetBlobModelName(const cv::Rect &roi);
//自适应参数区域更新:仿射变换到模板坐标系(基准矩形可为内定位/外定位)
int Adapt_Config(cv::RotatedRect tplRefRect, cv::RotatedRect curRefRect, cv::Mat& det_img);
// 计算 当前图 -> 模板图 / 模板图 -> 当前图 的仿射矩阵(基准矩形可为内定位/外定位)
// 注意: 不再对原图做 warpAffine, 只做坐标变换
int CalAffineMats(const cv::RotatedRect &tplRefRect, const cv::RotatedRect &curRefRect);
// 把模板 Pin 轮廓(pinImg 裁剪坐标系)反变换到当前检测图(detImg)坐标系
int TransformTplPinContoursToCur();
std::shared_ptr<AIFactory> AI_Factory;
@ -316,8 +319,12 @@ private:
cv::RotatedRect m_outer_rroi;
cv::RotatedRect m_inner_rroi;
cv::RotatedRect m_tplInnerRect; // 模板内边缘矩形
std::vector<std::vector<cv::Point>> m_tplPinContours; // 模板 pin blob 轮廓点集
std::vector<std::vector<cv::Point>> m_tplPinContours; // 模板 pin blob 轮廓点集(pinImg 裁剪坐标系)
std::vector<std::vector<cv::Point>> m_tplPinContoursCur; // 模板 pin 轮廓(反变换到当前检测图坐标系)
std::vector<std::vector<cv::Point>> m_curPinContours; // 当前 pin blob 轮廓点集
cv::Mat m_affine_cur2tpl; // 当前图 -> 模板图
cv::Mat m_affine_tpl2cur; // 模板图 -> 当前图
bool m_bAffineValid = false; // 仿射矩阵是否有效
std::vector<std::pair<cv::Point2f, cv::Point2f>> m_pinFitLines; // 新方案: 两排Pin拟合直线段(当前检测图坐标系, 两端点)
std::vector<cv::Point2f> m_pinMissMarks; // 新方案: 缺失Pin位置(相邻Pin中心连线中点)
};

@ -544,46 +544,101 @@ cv::Scalar ImgCheckAnalysisy::calc_blob_info_withstats(cv::Mat &img, const cv::M
return cv::Scalar(worb, energy, hj);
}
// 仿射变换到模板坐标系:把当前基准矩形(内定位/外定位)的顶点映射到模板对应基准矩形上
int ImgCheckAnalysisy::Adapt_Config(RotatedRect tplRefRect, RotatedRect curRefRect, Mat& det_img){
// 计算仿射矩阵(不做图像 warp):
// 基准点用当前基准矩形(内定位优先/外定位回退)的三个角点 -> 模板对应基准矩形的三个角点
// 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> tpl_vertices = CheckUtil::sort_vertices(tplRefRect);
cv::Point2f src_pts[3] = {
cur_vertices[0], // 对应左上
cur_vertices[1], // 对应右上
cur_vertices[2] // 对应左下
cv::Point2f cur_pts[3] = {
cur_vertices[0], // 左上
cur_vertices[1], // 右上
cur_vertices[2] // 左下
};
cv::Point2f dst_pts[3] = {
cv::Point2f tpl_pts[3] = {
tpl_vertices[0], // 左上
tpl_vertices[1], // 右上
tpl_vertices[2] // 左下
};
cv::Mat affine_mat = cv::getAffineTransform(src_pts, dst_pts);
// 仿射变换
cv::warpAffine(det_img, det_img, affine_mat, det_img.size());
m_affine_cur2tpl = cv::getAffineTransform(cur_pts, tpl_pts);
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变换到模板图像坐标系下
auto transformRotatedRect = [&affine_mat](cv::RotatedRect &rrect)
if (!m_bAffineValid)
{
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);
for (int i = 0; i < 4; i++)
// 把模板 Pin 轮廓反变换到当前检测图(detImg)坐标系:
// 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]);
float y = static_cast<float>(M[3] * pts[i].x + M[4] * pts[i].y + M[5]);
transformed_pts[i] = cv::Point2f(x, y);
// 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));
}
rrect = cv::minAreaRect(transformed_pts);
};
transformRotatedRect(m_outer_rroi);
transformRotatedRect(m_inner_rroi);
m_tplPinContoursCur.push_back(std::move(mapped));
}
return 0;
}
@ -701,13 +756,13 @@ int ImgCheckAnalysisy::CheckRun()
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);
// roi 用整张检测图区域(detImg 坐标系); detImg 现在取当前外定位包围框, 尺寸与之一致
cv::Rect curBound = m_outer_rroi.boundingRect();
alignErr.roi = cv::Rect(0, 0, curBound.width, curBound.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.flen = curBound.width * m_fImgage_Scale_X;
alignErr.fbreadth = curBound.height * m_fImgage_Scale_Y;
// 缺陷列表中原 other(其他)已移除;内外边缘对齐超差暂按 C型卡口异常 上报(待确认)
alignErr.nconfig_qx_type = CONFIG_QX_NAME_HunLiao;
@ -763,21 +818,44 @@ int ImgCheckAnalysisy::CheckRun()
}
#else
// 检测PIN偏移/缺失对位置基准最敏感: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)
{
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "Affine align basis: Inner(inner_rroi -> tplInnerRect)");
Adapt_Config(m_tplInnerRect, m_inner_rroi, m_CheckResult_shareP->in_shareImage->img);
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect",
"Affine align basis: Inner(inner_rroi -> tplInnerRect), template pins warp only, image NOT warped");
CalAffineMats(m_tplInnerRect, m_inner_rroi);
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "Affine align basis: Outer(outer_rroi -> tplOuterRect)");
Adapt_Config(m_tplOuterRect, m_outer_rroi, m_CheckResult_shareP->in_shareImage->img);
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect",
"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
m_Crop_Roi_paramImg = 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))});
}
#else
const std::vector<std::vector<cv::Point>> &tplDrawPinContours = m_tplPinContours;
const std::vector<std::vector<cv::Point>> &tplDrawPinContours = m_tplPinContoursCur;
#endif
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);
};
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());
if (nTpl > 0)
{
// 1、统计模板 pin 中心与外包矩形
// 1、统计模板 pin 中心与外包矩形(已反变换到当前检测图坐标系)
std::vector<cv::Point2f> tplCenters(nTpl);
std::vector<cv::Rect> tplRects(nTpl);
for (int i = 0; i < nTpl; i++)
{
tplCenters[i] = getCenter(m_tplPinContours[i]);
tplRects[i] = cv::boundingRect(m_tplPinContours[i]);
tplCenters[i] = getCenter(m_tplPinContoursCur[i]);
tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]);
}
// 2、当前 pin 中心与外包矩形
@ -2242,9 +2320,9 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
{
QX_ERROR_INFO_ pinErr;
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.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;
int longSide = (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);
}
#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
imwrite("pin_contours.png", showimg);
}

Loading…
Cancel
Save