|
|
|
|
@ -51,6 +51,14 @@
|
|
|
|
|
#define HANDLE_EDGE_MARGIN_STEP_COEF 3
|
|
|
|
|
// 把手缺失检测开关见 Base层 Function_Support_Det::bDetHandle(CheckConfigDefine.h)
|
|
|
|
|
|
|
|
|
|
// 支架边缘原图精细化默认参数(JSON: Support_Detect.form.support_param 下的 edge_refine_search / edge_refine_step):
|
|
|
|
|
// 仅对 edge_select 选中的边缘生效:沿边缘按步长采样,从边缘向产品内侧逐像素找第一个 255 像素作为新边缘点,
|
|
|
|
|
// 有效点足够时重新拟合边缘线(找不到的采样点没有新信息,不参与拟合,相当于沿用原边缘点)。
|
|
|
|
|
// edge_refine_search:最多向内搜索的像素数。搜不到 → 该采样点无贡献;调大更灵敏但可能搜到内部亮区
|
|
|
|
|
#define EDGE_REFINE_SEARCH_DEFAULT 80
|
|
|
|
|
// edge_refine_step:沿边采样步长(像素)。不需要每个点都找,调大可省时间;调小边缘拟合更稳
|
|
|
|
|
#define EDGE_REFINE_STEP_DEFAULT 40
|
|
|
|
|
|
|
|
|
|
// 分类映射配置文件(每行:label索引:缺陷名称;冒号后名称为该缺陷类型名称,行的顺序即缺陷类型索引)
|
|
|
|
|
static const std::string CLS_LABEL_CONFIG_FILE = "/home/aidlux/BOE/UseModel_ZB/BM_cls.txt";
|
|
|
|
|
|
|
|
|
|
@ -277,6 +285,92 @@ static cv::Mat MatAffineCompose(const cv::Mat &mA, const cv::Mat &mB)
|
|
|
|
|
return hC(cv::Rect(0, 0, 3, 2)).clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 稳健直线拟合:pts 为 (t, v) 采样点,拟合 v = k*t + b(左右边 t=y,v=x;上下边 t=x,v=y)
|
|
|
|
|
// 先最小二乘,再按“残差中位数的绝对偏差(MAD)”剔除离群点(把手凸起/边缘缺口/内部亮区等),重新拟合;
|
|
|
|
|
// 返回参与拟合的内点数,<4 表示拟合失败(调用方沿用原边缘)
|
|
|
|
|
static int FitLineRobustTV(const std::vector<cv::Point2f> &pts, double &fK, double &fB)
|
|
|
|
|
{
|
|
|
|
|
auto fnFit = [](const std::vector<cv::Point2f> &p, double &k, double &b) -> bool {
|
|
|
|
|
if (p.size() < 2)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
double fSumT = 0, fSumV = 0;
|
|
|
|
|
for (size_t i = 0; i < p.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
fSumT += p[i].x;
|
|
|
|
|
fSumV += p[i].y;
|
|
|
|
|
}
|
|
|
|
|
const double fMeanT = fSumT / p.size();
|
|
|
|
|
const double fMeanV = fSumV / p.size();
|
|
|
|
|
double fSxx = 0, fSxy = 0;
|
|
|
|
|
for (size_t i = 0; i < p.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
const double fDt = p[i].x - fMeanT;
|
|
|
|
|
fSxx += fDt * fDt;
|
|
|
|
|
fSxy += fDt * (p[i].y - fMeanV);
|
|
|
|
|
}
|
|
|
|
|
if (fSxx < 1e-6)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
k = fSxy / fSxx;
|
|
|
|
|
b = fMeanV - k * fMeanT;
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (pts.size() < 4)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
double fKFit = 0, fBFit = 0;
|
|
|
|
|
if (!fnFit(pts, fKFit, fBFit))
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<cv::Point2f> vecInlier;
|
|
|
|
|
for (int nIter = 0; nIter < 2; nIter++)
|
|
|
|
|
{
|
|
|
|
|
std::vector<double> vecRes;
|
|
|
|
|
vecRes.reserve(pts.size());
|
|
|
|
|
for (size_t i = 0; i < pts.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
vecRes.push_back(pts[i].y - (fKFit * pts[i].x + fBFit));
|
|
|
|
|
}
|
|
|
|
|
std::vector<double> vecAbs = vecRes;
|
|
|
|
|
std::nth_element(vecAbs.begin(), vecAbs.begin() + vecAbs.size() / 2, vecAbs.end());
|
|
|
|
|
const double fMed = vecAbs[vecAbs.size() / 2];
|
|
|
|
|
for (size_t i = 0; i < vecAbs.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
vecAbs[i] = std::fabs(vecAbs[i] - fMed);
|
|
|
|
|
}
|
|
|
|
|
std::nth_element(vecAbs.begin(), vecAbs.begin() + vecAbs.size() / 2, vecAbs.end());
|
|
|
|
|
const double fMad = vecAbs[vecAbs.size() / 2];
|
|
|
|
|
const double fThr = std::max(2.0, 3.0 * 1.4826 * fMad);
|
|
|
|
|
|
|
|
|
|
vecInlier.clear();
|
|
|
|
|
for (size_t i = 0; i < pts.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (std::fabs(pts[i].y - (fKFit * pts[i].x + fBFit)) <= fThr)
|
|
|
|
|
{
|
|
|
|
|
vecInlier.push_back(pts[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (vecInlier.size() < 4 || !fnFit(vecInlier, fKFit, fBFit))
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (vecInlier.size() < 4)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
fK = fKFit;
|
|
|
|
|
fB = fBFit;
|
|
|
|
|
return (int)vecInlier.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImgCheckAnalysisy::ImgCheckAnalysisy()
|
|
|
|
|
: m_pAI_Edge_Algin(m_pdetlog)
|
|
|
|
|
{
|
|
|
|
|
@ -1076,6 +1170,9 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
3、产品边缘:优先复用把手提取对 bigmask 做的"四边抗凸起拟合"主体四边形
|
|
|
|
|
(CheckHandleByBigMask -> GetMaskProtrusions;边缘上的凸起(把手等)不会把边缘带偏),
|
|
|
|
|
拟合不可用/无效时回退 bigmask 最大连通域外接矩形 rp(近似矩形,四周可能有凸起)
|
|
|
|
|
3.1、对 edge_select 选中的边缘再结合原图(detImg 灰度)精细化:沿边缘按步长采样,从边缘向产品内侧
|
|
|
|
|
找第一个 255 像素作为新边缘点(最多向内 edge_refine_search 像素,找不到沿用原边缘点),
|
|
|
|
|
有效点足够时稳健拟合新的边缘线;采样步长 edge_refine_step(像素)
|
|
|
|
|
4、按 edge_select 指定方向(left/right/up/down)计算 rs 中心到产品对应边缘的距离
|
|
|
|
|
5、计算值与标准值(x_standard/y_standard)做差取平均,平均差超过 offset(x_offset) → 偏移
|
|
|
|
|
6、旋转偏移:支架最小外接旋转矩形的长轴(对应左/右边缘)与产品左/右边缘夹角超过 r_offset(度) → 偏移 */
|
|
|
|
|
@ -1164,7 +1261,8 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
{
|
|
|
|
|
// 支架缺失 NG:ROI 取支架区域(有轮廓时)或整个产品区域(无轮廓时),映射到检测图(detImg)坐标
|
|
|
|
|
cv::Rect missRect = (rsRect.width > 0 && rsRect.height > 0) ? rsRect : bigRect;
|
|
|
|
|
cv::Rect roi = MatAffineRect(m_matSrc2Det, missRect);
|
|
|
|
|
// cv::Rect roi = MatAffineRect(m_matSrc2Det, missRect);
|
|
|
|
|
cv::Rect roi = missRect;
|
|
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
|
|
|
|
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
|
|
@ -1289,6 +1387,242 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
ptSegBArr[3] = cv::Point2f((float)(rpRect.x + rpRect.width), (float)(rpRect.y + rpRect.height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3.2、结合原图精细化检测边缘(只处理 edge_select 选中的方向):
|
|
|
|
|
// 在"所计算边缘"(拟合四边形边/回退外接矩形边)上按步长采样,从边缘向产品内侧逐像素扫描原图
|
|
|
|
|
// (detImg 灰度,未做其它处理;产品区域为饱和白),找到的第一个 255 像素作为该采样点的新边缘点;
|
|
|
|
|
// 最多向内搜索 edge_refine_search 像素(JSON,默认见 EDGE_REFINE_SEARCH_DEFAULT),
|
|
|
|
|
// 找不到的采样点没有新边缘信息,不参与拟合(相当于沿用原边缘点);
|
|
|
|
|
// edge_refine_step(JSON,默认见 EDGE_REFINE_STEP_DEFAULT)为沿边采样步长,不需要每个点都找;
|
|
|
|
|
// 有效点足够时稳健拟合新的边缘直线,更新测距点(中心行/列)与绘制线段。
|
|
|
|
|
{
|
|
|
|
|
const cv::Mat &grayEdgeImg = m_pImageAllResult->detImg;
|
|
|
|
|
int nRefineSearch = EDGE_REFINE_SEARCH_DEFAULT;
|
|
|
|
|
int nRefineStep = EDGE_REFINE_STEP_DEFAULT;
|
|
|
|
|
if (supportDet.edge_refine_search > 0)
|
|
|
|
|
{
|
|
|
|
|
nRefineSearch = (int)(supportDet.edge_refine_search + 0.5f);
|
|
|
|
|
}
|
|
|
|
|
if (supportDet.edge_refine_step > 0)
|
|
|
|
|
{
|
|
|
|
|
nRefineStep = (int)(supportDet.edge_refine_step + 0.5f);
|
|
|
|
|
}
|
|
|
|
|
nRefineSearch = std::max(1, nRefineSearch);
|
|
|
|
|
nRefineStep = std::max(4, nRefineStep);
|
|
|
|
|
|
|
|
|
|
bool bEdgeRefined[4] = {false, false, false, false}; // edge_select 配重复时只处理一次
|
|
|
|
|
if (grayEdgeImg.type() == CV_8UC1 && grayEdgeImg.cols > 0 && grayEdgeImg.rows > 0)
|
|
|
|
|
{
|
|
|
|
|
for (size_t iselect = 0; iselect < supportDet.edge_select.size(); iselect++)
|
|
|
|
|
{
|
|
|
|
|
const std::string &strEdge = supportDet.edge_select[iselect];
|
|
|
|
|
int gi = -1; // 方向索引:0 left 1 right 2 up 3 down
|
|
|
|
|
if (strEdge == "left")
|
|
|
|
|
{
|
|
|
|
|
gi = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (strEdge == "right")
|
|
|
|
|
{
|
|
|
|
|
gi = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (strEdge == "up")
|
|
|
|
|
{
|
|
|
|
|
gi = 2;
|
|
|
|
|
}
|
|
|
|
|
else if (strEdge == "down")
|
|
|
|
|
{
|
|
|
|
|
gi = 3;
|
|
|
|
|
}
|
|
|
|
|
if (gi < 0 || bEdgeRefined[gi])
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
bEdgeRefined[gi] = true;
|
|
|
|
|
|
|
|
|
|
// 当前边缘直线:左右边 x=k*y+b,上下边 y=k*x+b(与拟合四边形约定一致)
|
|
|
|
|
const bool bVertEdge = (gi == 0 || gi == 1);
|
|
|
|
|
const cv::Point2f ptSegA = ptSegAArr[gi];
|
|
|
|
|
const cv::Point2f ptSegB = ptSegBArr[gi];
|
|
|
|
|
const double fDelta = bVertEdge ? ((double)ptSegB.y - ptSegA.y) : ((double)ptSegB.x - ptSegA.x);
|
|
|
|
|
if (std::fabs(fDelta) < 4.0)
|
|
|
|
|
{
|
|
|
|
|
continue; // 边太短/退化,保持原边缘
|
|
|
|
|
}
|
|
|
|
|
const double fKOld = bVertEdge ? ((double)ptSegB.x - ptSegA.x) / fDelta
|
|
|
|
|
: ((double)ptSegB.y - ptSegA.y) / fDelta;
|
|
|
|
|
const double fBOld = bVertEdge ? (ptSegA.x - fKOld * ptSegA.y)
|
|
|
|
|
: (ptSegA.y - fKOld * ptSegA.x);
|
|
|
|
|
|
|
|
|
|
// 沿边采样区间:两端各去掉 5%(至少 4px),避开圆角过渡段
|
|
|
|
|
const double fT0 = bVertEdge ? std::min(ptSegA.y, ptSegB.y) : std::min(ptSegA.x, ptSegB.x);
|
|
|
|
|
const double fT1 = bVertEdge ? std::max(ptSegA.y, ptSegB.y) : std::max(ptSegA.x, ptSegB.x);
|
|
|
|
|
const double fTrim = std::max(4.0, (fT1 - fT0) * 0.05);
|
|
|
|
|
const double fTStart = fT0 + fTrim;
|
|
|
|
|
const double fTEnd = fT1 - fTrim;
|
|
|
|
|
if (fTEnd - fTStart < 8.0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<cv::Point2f> vecRefinePts; // 有效的新边缘点(t, v):左右 t=y/v=x,上下 t=x/v=y
|
|
|
|
|
int nSampleNum = 0;
|
|
|
|
|
for (double fT = fTStart; fT <= fTEnd; fT += nRefineStep)
|
|
|
|
|
{
|
|
|
|
|
nSampleNum++;
|
|
|
|
|
const double fEdgePos = fKOld * fT + fBOld;
|
|
|
|
|
const cv::Point2f ptSrc = bVertEdge ? cv::Point2f((float)fEdgePos, (float)fT)
|
|
|
|
|
: cv::Point2f((float)fT, (float)fEdgePos);
|
|
|
|
|
// 原图坐标 -> 检测图(detImg)坐标(纯平移);在检测图灰度上找第一个 255
|
|
|
|
|
const cv::Point2f ptDet = MatAffinePoint(m_matSrc2Det, ptSrc);
|
|
|
|
|
const int nFix = bVertEdge ? cvRound(ptDet.y) : cvRound(ptDet.x); // 固定坐标(行/列)
|
|
|
|
|
if ((bVertEdge && (nFix < 0 || nFix >= grayEdgeImg.rows)) ||
|
|
|
|
|
(!bVertEdge && (nFix < 0 || nFix >= grayEdgeImg.cols)))
|
|
|
|
|
{
|
|
|
|
|
continue; // 固定坐标越界,该采样点无法搜索
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bFound = false;
|
|
|
|
|
double fNewPos = fEdgePos; // 找到的新边缘位置(原图坐标)
|
|
|
|
|
if (gi == 0) // 左边:向产品内侧(+x)找
|
|
|
|
|
{
|
|
|
|
|
const int nX0 = (int)std::ceil(ptDet.x);
|
|
|
|
|
const int nXEnd = std::min(grayEdgeImg.cols - 1, nX0 + nRefineSearch);
|
|
|
|
|
for (int x = std::max(0, nX0); x <= nXEnd; x++)
|
|
|
|
|
{
|
|
|
|
|
if (grayEdgeImg.at<uchar>(nFix, x) == 255)
|
|
|
|
|
{
|
|
|
|
|
const cv::Point2f ptNew = MatAffinePoint(m_matDet2Src, cv::Point2f((float)x, ptDet.y));
|
|
|
|
|
fNewPos = ptNew.x;
|
|
|
|
|
bFound = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (gi == 1) // 右边:向产品内侧(-x)找
|
|
|
|
|
{
|
|
|
|
|
const int nX0 = (int)std::floor(ptDet.x);
|
|
|
|
|
const int nXEnd = std::max(0, nX0 - nRefineSearch);
|
|
|
|
|
for (int x = std::min(grayEdgeImg.cols - 1, nX0); x >= nXEnd && x >= 0; x--)
|
|
|
|
|
{
|
|
|
|
|
if (grayEdgeImg.at<uchar>(nFix, x) == 255)
|
|
|
|
|
{
|
|
|
|
|
const cv::Point2f ptNew = MatAffinePoint(m_matDet2Src, cv::Point2f((float)x, ptDet.y));
|
|
|
|
|
fNewPos = ptNew.x;
|
|
|
|
|
bFound = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (gi == 2) // 上边:向产品内侧(+y)找
|
|
|
|
|
{
|
|
|
|
|
const int nY0 = (int)std::ceil(ptDet.y);
|
|
|
|
|
const int nYEnd = std::min(grayEdgeImg.rows - 1, nY0 + nRefineSearch);
|
|
|
|
|
for (int y = std::max(0, nY0); y <= nYEnd; y++)
|
|
|
|
|
{
|
|
|
|
|
if (grayEdgeImg.at<uchar>(y, nFix) == 255)
|
|
|
|
|
{
|
|
|
|
|
const cv::Point2f ptNew = MatAffinePoint(m_matDet2Src, cv::Point2f(ptDet.x, (float)y));
|
|
|
|
|
fNewPos = ptNew.y;
|
|
|
|
|
bFound = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // 下边:向产品内侧(-y)找
|
|
|
|
|
{
|
|
|
|
|
const int nY0 = (int)std::floor(ptDet.y);
|
|
|
|
|
const int nYEnd = std::max(0, nY0 - nRefineSearch);
|
|
|
|
|
for (int y = std::min(grayEdgeImg.rows - 1, nY0); y >= nYEnd && y >= 0; y--)
|
|
|
|
|
{
|
|
|
|
|
if (grayEdgeImg.at<uchar>(y, nFix) == 255)
|
|
|
|
|
{
|
|
|
|
|
const cv::Point2f ptNew = MatAffinePoint(m_matDet2Src, cv::Point2f(ptDet.x, (float)y));
|
|
|
|
|
fNewPos = ptNew.y;
|
|
|
|
|
bFound = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (bFound)
|
|
|
|
|
{
|
|
|
|
|
vecRefinePts.push_back(cv::Point2f((float)fT, (float)fNewPos));
|
|
|
|
|
}
|
|
|
|
|
// 找不到:该采样点没有新边缘信息,不参与拟合(相当于沿用原边缘点)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 有效采样点足够时重新拟合边缘线;否则保留原边缘
|
|
|
|
|
if (vecRefinePts.size() < 4)
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "支架检测",
|
|
|
|
|
"edge refine %s skip: sample %d found %ld (need 4), keep old",
|
|
|
|
|
strEdge.c_str(), nSampleNum, (long)vecRefinePts.size());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
double fKNew = 0, fBNew = 0;
|
|
|
|
|
if (FitLineRobustTV(vecRefinePts, fKNew, fBNew) < 4)
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "支架检测",
|
|
|
|
|
"edge refine %s skip: fit failed, keep old", strEdge.c_str());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 中心行/列上的新边缘位置 + 合理性检查(仍需在中心外侧,相对原边缘偏移不超过搜索范围)
|
|
|
|
|
const double fOldCenterPos = bVertEdge ? ptEdgeArr[gi].x : ptEdgeArr[gi].y;
|
|
|
|
|
const double fNewCenterPos = bVertEdge ? (fKNew * fCy + fBNew) : (fKNew * fCx + fBNew);
|
|
|
|
|
const bool bCenterSideOK = (gi == 0) ? (fNewCenterPos < fCx)
|
|
|
|
|
: (gi == 1) ? (fNewCenterPos > fCx)
|
|
|
|
|
: (gi == 2) ? (fNewCenterPos < fCy)
|
|
|
|
|
: (fNewCenterPos > fCy);
|
|
|
|
|
if (!bCenterSideOK || std::fabs(fNewCenterPos - fOldCenterPos) > nRefineSearch + 2.0)
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "支架检测",
|
|
|
|
|
"edge refine %s reject: center %.1f -> %.1f (limit %d), keep old",
|
|
|
|
|
strEdge.c_str(), fOldCenterPos, fNewCenterPos, nRefineSearch);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 应用新边缘线:更新测距点(中心行/列)与绘制线段两端(沿边坐标不变,位置取新线)
|
|
|
|
|
if (bVertEdge)
|
|
|
|
|
{
|
|
|
|
|
ptEdgeArr[gi].x = (float)fNewCenterPos;
|
|
|
|
|
ptSegAArr[gi].x = (float)(fKNew * ptSegAArr[gi].y + fBNew);
|
|
|
|
|
ptSegBArr[gi].x = (float)(fKNew * ptSegBArr[gi].y + fBNew);
|
|
|
|
|
if (gi == 0)
|
|
|
|
|
{
|
|
|
|
|
fDL = fCx - ptEdgeArr[0].x;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fDR = ptEdgeArr[1].x - fCx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ptEdgeArr[gi].y = (float)fNewCenterPos;
|
|
|
|
|
ptSegAArr[gi].y = (float)(fKNew * ptSegAArr[gi].x + fBNew);
|
|
|
|
|
ptSegBArr[gi].y = (float)(fKNew * ptSegBArr[gi].x + fBNew);
|
|
|
|
|
if (gi == 2)
|
|
|
|
|
{
|
|
|
|
|
fDT = fCy - ptEdgeArr[2].y;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fDB = ptEdgeArr[3].y - fCy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "支架检测",
|
|
|
|
|
"edge refine %s: sample %d found %ld center %.1f -> %.1f shift %.1f",
|
|
|
|
|
strEdge.c_str(), nSampleNum, (long)vecRefinePts.size(),
|
|
|
|
|
fOldCenterPos, fNewCenterPos, fNewCenterPos - fOldCenterPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (!supportDet.edge_select.empty())
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "支架检测",
|
|
|
|
|
"edge refine skip: detImg not CV_8UC1 (type %d)", grayEdgeImg.type());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3.5、旋转偏移:支架最小外接旋转矩形(长轴对应左/右边缘、短轴对应上/下边缘)
|
|
|
|
|
// 长轴与产品左/右边缘的夹角 > r_offset(度) → 偏移;r_offset<=0 不判定(兼容旧参数)
|
|
|
|
|
float fSupportAngle = 0; // 支架长轴相对产品左/右边缘的夹角(度,带符号;正=支架长轴相对产品边缘向右倾)
|
|
|
|
|
@ -1446,7 +1780,8 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
|
|
|
|
|
|
|
// roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系
|
|
|
|
|
cv::Rect roi = MatAffineRect(m_matSrc2Det, rsRect);
|
|
|
|
|
// cv::Rect roi = MatAffineRect(m_matSrc2Det, rsRect);
|
|
|
|
|
cv::Rect roi = rsRect;
|
|
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
|
|
temerror.roi = roi;
|
|
|
|
|
temerror.area = roi.width * roi.height;
|
|
|
|
|
@ -1499,7 +1834,8 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 上报该把手区域的位置:参数(模板)坐标 -> 检测图(detImg)坐标
|
|
|
|
|
cv::Rect roi = MatAffineRect(m_matParam2Det, regionResult.boxRegion);
|
|
|
|
|
// cv::Rect roi = MatAffineRect(m_matParam2Det, regionResult.boxRegion);
|
|
|
|
|
cv::Rect roi = regionResult.boxRegion;
|
|
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
|
|
if (roi.width <= 0 || roi.height <= 0)
|
|
|
|
|
{
|
|
|
|
|
|