|
|
|
@ -884,82 +884,189 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "3、pre detect", "-------------------------pre Det--------------- \n");
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "3、pre detect", "-------------------------pre Det--------------- \n");
|
|
|
|
|
|
|
|
|
|
|
|
/* 检测支架偏移(bDetSupport 开关关闭时不做支架判定) */
|
|
|
|
/* 检测支架偏移(bDetSupport 开关关闭时不做支架判定)
|
|
|
|
|
|
|
|
不使用参数绘制区域,结合两次定位的 mask 计算:
|
|
|
|
|
|
|
|
1、bigmask 最大连通域 = 产品区域 rp(近似矩形,四周可能有凸起)
|
|
|
|
|
|
|
|
2、smallmask 最大连通域 = 支架区域 rs(按定位裁剪区 m_outer_rroi 外接矩形映射回原图坐标)
|
|
|
|
|
|
|
|
3、按 edge_select 指定方向(left/right/up/down)计算 rs 中心到 rp 对应边缘的距离
|
|
|
|
|
|
|
|
4、计算值与标准值(x_standard/y_standard)做差取平均,平均差超过 offset(x_offset) → 偏移 */
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Function_Support_Det &supportDet = m_pFuntion->function.f_supportDet;
|
|
|
|
Function_Support_Det &supportDet = m_pFuntion->function.f_supportDet;
|
|
|
|
if (supportDet.bOpen && supportDet.bDetSupport &&
|
|
|
|
if (supportDet.bOpen && supportDet.bDetSupport)
|
|
|
|
supportDet.supportRect.size.width > 0 && supportDet.supportRect.size.height > 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const cv::Mat &bigmask = m_pEdge_Align_Result->bigmask;
|
|
|
|
cv::RotatedRect &inner = m_inner_rroi;
|
|
|
|
const cv::Mat &smallmask = m_pEdge_Align_Result->smallmask;
|
|
|
|
cv::RotatedRect &support = supportDet.supportRect;
|
|
|
|
if (bigmask.empty() || smallmask.empty())
|
|
|
|
|
|
|
|
|
|
|
|
// x、y 中心偏移
|
|
|
|
|
|
|
|
float dx = fabs(inner.center.x - support.center.x);
|
|
|
|
|
|
|
|
float dy = fabs(inner.center.y - support.center.y);
|
|
|
|
|
|
|
|
// 旋转偏移(矩形旋转 90 度等价,角度取最小差)
|
|
|
|
|
|
|
|
float dangle = fabs(inner.angle - support.angle);
|
|
|
|
|
|
|
|
if (dangle > 45.0f)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
dangle = fabs(dangle - 90.0f);
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "mask empty (big %d small %d), skip support offset", bigmask.empty(), smallmask.empty());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
bool bOffset = (dx > supportDet.x_offset) || (dy > supportDet.y_offset) || (dangle > supportDet.r_offset);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 缺失判定:内边缘面积小于支架模板面积的 75%
|
|
|
|
|
|
|
|
float innerArea = inner.size.width * inner.size.height;
|
|
|
|
|
|
|
|
float supportArea = support.size.width * support.size.height;
|
|
|
|
|
|
|
|
bool bLoss = (supportArea > 0.0f) && (innerArea < supportArea * 0.75f);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bOffset || bLoss)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
|
// 1、mask 二值化(兼容 0/1 与 0/255 掩膜)
|
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
|
cv::Mat bigBin, smallBin;
|
|
|
|
|
|
|
|
|
|
|
|
// roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系
|
|
|
|
|
|
|
|
cv::Rect roi = MatAffineRect(m_matSrc2Det, inner.boundingRect());
|
|
|
|
|
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
|
|
|
|
|
temerror.roi = roi;
|
|
|
|
|
|
|
|
temerror.area = roi.width * roi.height;
|
|
|
|
|
|
|
|
temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
temerror.JudgArea_second = temerror.JudgArea;
|
|
|
|
|
|
|
|
float w = inner.size.width;
|
|
|
|
|
|
|
|
float h = inner.size.height;
|
|
|
|
|
|
|
|
temerror.flen = (w > h ? w : h) * m_fImgage_Scale_X;
|
|
|
|
|
|
|
|
temerror.fbreadth = (w > h ? h : w) * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bLoss)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_support_loss;
|
|
|
|
cv::Mat grayMask;
|
|
|
|
|
|
|
|
if (bigmask.channels() != 1)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::cvtColor(bigmask, grayMask, cv::COLOR_BGR2GRAY);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
grayMask = bigmask;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
double dMin = 0;
|
|
|
|
|
|
|
|
double dMax = 0;
|
|
|
|
|
|
|
|
cv::minMaxLoc(grayMask, &dMin, &dMax);
|
|
|
|
|
|
|
|
cv::threshold(grayMask, bigBin, dMax > 1 ? dMax * 0.5 : 0, 255, cv::THRESH_BINARY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_support_offset;
|
|
|
|
cv::Mat grayMask;
|
|
|
|
|
|
|
|
if (smallmask.channels() != 1)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::cvtColor(smallmask, grayMask, cv::COLOR_BGR2GRAY);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
grayMask = smallmask;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
double dMin = 0;
|
|
|
|
|
|
|
|
double dMax = 0;
|
|
|
|
|
|
|
|
cv::minMaxLoc(grayMask, &dMin, &dMax);
|
|
|
|
|
|
|
|
cv::threshold(grayMask, smallBin, dMax > 1 ? dMax * 0.5 : 0, 255, cv::THRESH_BINARY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
temerror.qx_name = CONFIG_QX_NAME_Names[temerror.nconfig_qx_type];
|
|
|
|
|
|
|
|
temerror.result = QX_RESULT_TYPE_NG;
|
|
|
|
|
|
|
|
temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算支架中心所在的检测区域
|
|
|
|
// 2、最大连通域轮廓:bigmask -> 产品区域 rp(原图坐标);smallmask -> 支架区域 rs(smallmask 局部坐标)
|
|
|
|
cv::Point pCenter;
|
|
|
|
bool bFoundRp = false;
|
|
|
|
pCenter.x = roi.x + roi.width * 0.5;
|
|
|
|
std::vector<cv::Point> rpContour = CheckUtil::getLargestContourROI(bigBin, bFoundRp);
|
|
|
|
pCenter.y = roi.y + roi.height * 0.5;
|
|
|
|
bool bFoundRs = false;
|
|
|
|
int nmaxregionIdx = 0;
|
|
|
|
std::vector<cv::Point> rsContour = CheckUtil::getLargestContourROI(smallBin, bFoundRs);
|
|
|
|
for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++)
|
|
|
|
if (!bFoundRp || rpContour.size() < 3 || !bFoundRs || rsContour.size() < 3)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const std::vector<cv::Point> &polygon = m_DetRoiList.roiList_Src[iregion];
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "contour invalid (rp %d %ld, rs %d %ld), skip support offset",
|
|
|
|
double result = cv::pointPolygonTest(polygon, pCenter, false);
|
|
|
|
bFoundRp, rpContour.size(), bFoundRs, rsContour.size());
|
|
|
|
if (result < 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
nmaxregionIdx = iregion;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
temerror.detRegionidxList.push_back(nmaxregionIdx);
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// rp 边缘:产品最大连通域的外接矩形(含凸起)
|
|
|
|
|
|
|
|
cv::Rect rpRect = cv::boundingRect(rpContour);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// rs 映射回原图坐标:smallmask 对应定位时裁剪的大轮廓区域(m_outer_rroi 外接矩形),按比例映射
|
|
|
|
|
|
|
|
cv::Rect bigRect = m_outer_rroi.boundingRect();
|
|
|
|
|
|
|
|
float fx = bigRect.width * 1.0f / smallmask.cols;
|
|
|
|
|
|
|
|
float fy = bigRect.height * 1.0f / smallmask.rows;
|
|
|
|
|
|
|
|
cv::Rect rsSmallRect = cv::boundingRect(rsContour);
|
|
|
|
|
|
|
|
cv::Rect rsRect(
|
|
|
|
|
|
|
|
cvRound(bigRect.x + rsSmallRect.x * fx),
|
|
|
|
|
|
|
|
cvRound(bigRect.y + rsSmallRect.y * fy),
|
|
|
|
|
|
|
|
cvRound(rsSmallRect.width * fx),
|
|
|
|
|
|
|
|
cvRound(rsSmallRect.height * fy));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3、rs 中心到 rp 上下左右边缘的距离
|
|
|
|
|
|
|
|
float fCx = rsRect.x + rsRect.width * 0.5f;
|
|
|
|
|
|
|
|
float fCy = rsRect.y + rsRect.height * 0.5f;
|
|
|
|
|
|
|
|
float fDL = fCx - rpRect.x; // 到左边缘
|
|
|
|
|
|
|
|
float fDR = (rpRect.x + rpRect.width) - fCx; // 到右边缘
|
|
|
|
|
|
|
|
float fDT = fCy - rpRect.y; // 到上边缘
|
|
|
|
|
|
|
|
float fDB = (rpRect.y + rpRect.height) - fCy; // 到下边缘
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4、按 edge_select 指定方向(left/right/up/down)取计算值,与标准值做差取平均,平均差超过 offset → 偏移
|
|
|
|
|
|
|
|
// left/right 用 x_standard,up/down 用 y_standard;offset 用 x_offset
|
|
|
|
|
|
|
|
float fSumDiff = 0;
|
|
|
|
|
|
|
|
int nDiffNum = 0;
|
|
|
|
|
|
|
|
std::string strSelect = "";
|
|
|
|
|
|
|
|
std::string strDiff = "";
|
|
|
|
|
|
|
|
char strTmp[64];
|
|
|
|
|
|
|
|
for (size_t iselect = 0; iselect < supportDet.edge_select.size(); iselect++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const std::string &strEdge = supportDet.edge_select[iselect];
|
|
|
|
|
|
|
|
float fDist = 0;
|
|
|
|
|
|
|
|
float fStandard = 0;
|
|
|
|
|
|
|
|
if (strEdge == "left")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fDist = fDL;
|
|
|
|
|
|
|
|
fStandard = supportDet.x_standard;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (strEdge == "right")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fDist = fDR;
|
|
|
|
|
|
|
|
fStandard = supportDet.x_standard;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (strEdge == "up")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fDist = fDT;
|
|
|
|
|
|
|
|
fStandard = supportDet.y_standard;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (strEdge == "down")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fDist = fDB;
|
|
|
|
|
|
|
|
fStandard = supportDet.y_standard;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
|
float fDiff = fabs(fDist - fStandard);
|
|
|
|
|
|
|
|
fSumDiff += fDiff;
|
|
|
|
|
|
|
|
nDiffNum++;
|
|
|
|
|
|
|
|
sprintf(strTmp, "%s(%.1f/%.1f) ", strEdge.c_str(), fDist, fStandard);
|
|
|
|
|
|
|
|
strSelect += strTmp;
|
|
|
|
|
|
|
|
sprintf(strTmp, "%.1f ", fDiff);
|
|
|
|
|
|
|
|
strDiff += strTmp;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// edge_select 为空或无效时 nDiffNum=0,结果不参与判定(仅打日志)
|
|
|
|
|
|
|
|
float fAvgDiff = (nDiffNum > 0) ? (fSumDiff / nDiffNum) : 0.0f;
|
|
|
|
|
|
|
|
bool bOffset = (nDiffNum > 0) && (fAvgDiff > supportDet.x_offset);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测",
|
|
|
|
|
|
|
|
"rp [%d %d %d %d] rs [%d %d %d %d] scale (%.4f %.4f) center (%.1f %.1f) dL %.1f dR %.1f dT %.1f dB %.1f | select %s diff %s avgDiff %.1f limit %.1f ng %d",
|
|
|
|
|
|
|
|
rpRect.x, rpRect.y, rpRect.width, rpRect.height,
|
|
|
|
|
|
|
|
rsRect.x, rsRect.y, rsRect.width, rsRect.height,
|
|
|
|
|
|
|
|
fx, fy, fCx, fCy, fDL, fDR, fDT, fDB,
|
|
|
|
|
|
|
|
strSelect.c_str(), strDiff.c_str(), fAvgDiff, supportDet.x_offset, bOffset);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bOffset)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
|
|
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系
|
|
|
|
|
|
|
|
cv::Rect roi = MatAffineRect(m_matSrc2Det, rsRect);
|
|
|
|
|
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
|
|
|
|
|
temerror.roi = roi;
|
|
|
|
|
|
|
|
temerror.area = roi.width * roi.height;
|
|
|
|
|
|
|
|
temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
temerror.JudgArea_second = temerror.JudgArea;
|
|
|
|
|
|
|
|
float w = rsRect.width;
|
|
|
|
|
|
|
|
float h = rsRect.height;
|
|
|
|
|
|
|
|
temerror.flen = (w > h ? w : h) * m_fImgage_Scale_X;
|
|
|
|
|
|
|
|
temerror.fbreadth = (w > h ? h : w) * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_support_offset;
|
|
|
|
|
|
|
|
temerror.qx_name = CONFIG_QX_NAME_Names[temerror.nconfig_qx_type];
|
|
|
|
|
|
|
|
temerror.result = QX_RESULT_TYPE_NG;
|
|
|
|
|
|
|
|
temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算支架中心所在的检测区域
|
|
|
|
|
|
|
|
cv::Point pCenter;
|
|
|
|
|
|
|
|
pCenter.x = roi.x + roi.width * 0.5;
|
|
|
|
|
|
|
|
pCenter.y = roi.y + roi.height * 0.5;
|
|
|
|
|
|
|
|
int nmaxregionIdx = 0;
|
|
|
|
|
|
|
|
for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const std::vector<cv::Point> &polygon = m_DetRoiList.roiList_Src[iregion];
|
|
|
|
|
|
|
|
double result = cv::pointPolygonTest(polygon, pCenter, false);
|
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
nmaxregionIdx = iregion;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
temerror.detRegionidxList.push_back(nmaxregionIdx);
|
|
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "%s %d roi [%d %d %d %d] dx %f dy %f dangle %f",
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
|
temerror.qx_name.c_str(), temerror.Idx, roi.x, roi.y, roi.width, roi.height, dx, dy, dangle);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|