update 支架偏移改为计算到产品边缘

dev_lsy
liusiyang 4 weeks ago
parent c8ffa74833
commit 58563d817c

@ -295,7 +295,6 @@ int AI_Edge_Algin::Detect(const cv::Mat &img, DetConfig *pDetConfig, std::shared
m_pCheckResult_Aling->bigmask = Big_outimg;
m_pCheckResult_Aling->smallmask = Small_outimg;
m_pCheckResult_Aling->tagmask = Tag_outimg;
pCheckResult_Aling = m_pCheckResult_Aling;
return 0;

@ -884,57 +884,165 @@ int ImgCheckAnalysisy::CheckRun()
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;
if (supportDet.bOpen && supportDet.bDetSupport &&
supportDet.supportRect.size.width > 0 && supportDet.supportRect.size.height > 0)
if (supportDet.bOpen && supportDet.bDetSupport)
{
cv::RotatedRect &inner = m_inner_rroi;
cv::RotatedRect &support = supportDet.supportRect;
const cv::Mat &bigmask = m_pEdge_Align_Result->bigmask;
const cv::Mat &smallmask = m_pEdge_Align_Result->smallmask;
if (bigmask.empty() || smallmask.empty())
{
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "mask empty (big %d small %d), skip support offset", bigmask.empty(), smallmask.empty());
}
else
{
// 1、mask 二值化(兼容 0/1 与 0/255 掩膜)
cv::Mat bigBin, smallBin;
{
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);
}
{
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);
}
// 2、最大连通域轮廓:bigmask -> 产品区域 rp(原图坐标);smallmask -> 支架区域 rs(smallmask 局部坐标)
bool bFoundRp = false;
std::vector<cv::Point> rpContour = CheckUtil::getLargestContourROI(bigBin, bFoundRp);
bool bFoundRs = false;
std::vector<cv::Point> rsContour = CheckUtil::getLargestContourROI(smallBin, bFoundRs);
if (!bFoundRp || rpContour.size() < 3 || !bFoundRs || rsContour.size() < 3)
{
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "contour invalid (rp %d %ld, rs %d %ld), skip support offset",
bFoundRp, rpContour.size(), bFoundRs, rsContour.size());
}
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));
// 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)
// 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")
{
dangle = fabs(dangle - 90.0f);
fDist = fDB;
fStandard = supportDet.y_standard;
}
else
{
continue;
}
bool bOffset = (dx > supportDet.x_offset) || (dy > supportDet.y_offset) || (dangle > supportDet.r_offset);
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);
// 缺失判定:内边缘面积小于支架模板面积的 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);
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 || bLoss)
if (bOffset)
{
QX_ERROR_INFO_ temerror;
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
// roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系
cv::Rect roi = MatAffineRect(m_matSrc2Det, inner.boundingRect());
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 = inner.size.width;
float h = inner.size.height;
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;
if (bLoss)
{
temerror.nconfig_qx_type = CONFIG_QX_NAME_support_loss;
}
else
{
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];
@ -957,9 +1065,8 @@ int ImgCheckAnalysisy::CheckRun()
temerror.detRegionidxList.push_back(nmaxregionIdx);
m_pDetResult->pQx_ErrorList->push_back(temerror);
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "%s %d roi [%d %d %d %d] dx %f dy %f dangle %f",
temerror.qx_name.c_str(), temerror.Idx, roi.x, roi.y, roi.width, roi.height, dx, dy, dangle);
}
}
}
}
}

@ -945,6 +945,9 @@ struct Function_Support_Det
bool bDetSupport;
std::vector<cv::Point> supportRegion;
cv::RotatedRect supportRect;
float x_standard; // 水平方向标准距离(left/right 用)
float y_standard; // 垂直方向标准距离(up/down 用)
std::vector<std::string> edge_select; // 参与计算的方向:up/down/left/right
float x_offset;
float y_offset;
float r_offset;
@ -966,6 +969,9 @@ struct Function_Support_Det
bDetSupport = false;
supportRegion.clear();
supportRect = cv::RotatedRect();
x_standard = 0;
y_standard = 0;
edge_select.clear();
x_offset = 0;
y_offset = 0;
r_offset = 0;
@ -980,6 +986,9 @@ struct Function_Support_Det
this->bDetSupport = tem.bDetSupport;
this->supportRegion.assign(tem.supportRegion.begin(), tem.supportRegion.end());
this->supportRect = tem.supportRect;
this->x_standard = tem.x_standard;
this->y_standard = tem.y_standard;
this->edge_select.assign(tem.edge_select.begin(), tem.edge_select.end());
this->x_offset = tem.x_offset;
this->y_offset = tem.y_offset;
this->r_offset = tem.r_offset;
@ -990,14 +999,14 @@ struct Function_Support_Det
}
void print(std::string str)
{
printf("%s>>bOpen %d bDetSupport %d bDetHandle %d bDetTag %d x_offset %f y_offset %f r_offset %f handleRegion num %ld\n",
str.c_str(), bOpen, bDetSupport, bDetHandle, bDetTag, x_offset, y_offset, r_offset, handleRegions.size());
printf("%s>>bOpen %d bDetSupport %d bDetHandle %d bDetTag %d x_standard %f y_standard %f x_offset %f y_offset %f r_offset %f edge num %ld handleRegion num %ld\n",
str.c_str(), bOpen, bDetSupport, bDetHandle, bDetTag, x_standard, y_standard, x_offset, y_offset, r_offset, edge_select.size(), handleRegions.size());
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%s>>bOpen %d bDetSupport %d bDetHandle %d bDetTag %d x_offset %f y_offset %f r_offset %f handleRegion num %ld\n",
str.c_str(), bOpen, bDetSupport, bDetHandle, bDetTag, x_offset, y_offset, r_offset, handleRegions.size());
sprintf(buffer, "%s>>bOpen %d bDetSupport %d bDetHandle %d bDetTag %d x_standard %f y_standard %f x_offset %f y_offset %f r_offset %f edge num %ld handleRegion num %ld\n",
str.c_str(), bOpen, bDetSupport, bDetHandle, bDetTag, x_standard, y_standard, x_offset, y_offset, r_offset, edge_select.size(), handleRegions.size());
std::string str123 = buffer;
return str123;
}

@ -443,6 +443,25 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
}
}
if (value_f["form"]["support_param"]["x_standard"])
{
function.f_supportDet.x_standard = value_f["form"]["support_param"]["x_standard"].asFloat();
}
if (value_f["form"]["support_param"]["y_standard"])
{
function.f_supportDet.y_standard = value_f["form"]["support_param"]["y_standard"].asFloat();
}
auto value_qx = value_f["form"]["support_param"]["edge_select"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
function.f_supportDet.edge_select.push_back(channel);
}
}
if (value_f["form"]["support_param"]["x_offset"])
{
function.f_supportDet.x_offset = value_f["form"]["support_param"]["x_offset"].asFloat();

Loading…
Cancel
Save