update 优化支架相关绘制

dev_lsy
liusiyang 3 weeks ago
parent 512261dd60
commit 1ce1c502e8

@ -80,8 +80,18 @@ public:
// 抠出掩膜四周的凸起:主体近似矩形,凸起在该边表现为边界值的一次突变(进入)与一次反向突变(离开) // 抠出掩膜四周的凸起:主体近似矩形,凸起在该边表现为边界值的一次突变(进入)与一次反向突变(离开)
// binMask:掩膜(非零为前景,兼容 0/1 与 0/255);protrusions:输出的凸起列表 // binMask:掩膜(非零为前景,兼容 0/1 与 0/255);protrusions:输出的凸起列表
// nJumpThresh:边界突变阈值(输入图像素);nMinRun:凸起最小长度(输入图像素);nMinDepth:凸起最小深度(输入图像素) // 参数调整影响(nJumpThresh/nMinRun/nMinDepth 为输入图像素,内部按降采样比例 nScale(1/2/4) 缩小后使用;nMaxRun 不换算,直接用工作图像素):
// nMaxRun:凸起最大长度(输入图像素),<=0 时取 掩膜短边/3 // nJumpThresh:边界突变阈值,相邻行/列边界值一次变化超过它才算"进入/离开"台阶;
// 调大:只认陡峭台阶,圆角/噪声/锯齿被过滤,过大时边缘圆滑的真把手会漏检;
// 调小:敏感,噪声/斜边易触发假配对(可能形成跨度很大的假凸起)。
// nMinRun:凸起最小长度("离开"突变搜索从 s+nMinRun 开始;profile 比它短的边直接跳过);
// 调大:短凸起被排除,若大于真把手长度,真"离开"突变被跳过、会配到更远突变 → 出更大的框;
// 调小:小抖动/碎框变多。
// nMinDepth:凸起最小深度,区间内 ≥80% 采样点相对首尾连线基线外偏 ≥ 它(且最大外偏 ≥ 它)才保留(滤斜边/圆角);
// 调大:只保留明显凸出的,浅凸起漏检,可挡"浅而长"的假配对;
// 调小:浅波纹/边缘抖动也过检,假凸起变多。
// nMaxRun:凸起最大长度(框沿边长度的硬上限);<=0 时取 max(100, 掩膜短边/3)(工作图像素);
// 调大:允许远距离突变配对 → 会出"覆盖整边"的大框;调小:框被截短,小于真把手长度时真把手漏检。
// 返回:0 成功、1 掩膜为空、2 无有效前景 // 返回:0 成功、1 掩膜为空、2 无有效前景
static int GetMaskProtrusions(const cv::Mat &binMask, std::vector<Mask_Protrusion_> &protrusions, static int GetMaskProtrusions(const cv::Mat &binMask, std::vector<Mask_Protrusion_> &protrusions,
int nJumpThresh = 40, int nMinRun = 40, int nMinDepth = 40, int nMaxRun = 0); int nJumpThresh = 40, int nMinRun = 40, int nMinDepth = 40, int nMaxRun = 0);

@ -79,6 +79,45 @@ struct Handle_Region_Result_
} }
}; };
// 支架偏移检测的绘制信息(CheckRun 支架块计算 -> DrawResult_Step_1 绘制)
// 坐标均为原图坐标,绘制时经 m_matSrc2Det 映射到检测图后再缩放到结果图
struct Support_Draw_Info_
{
// 参与判定的单个方向:实测/标准距离 + 是否超标准(超标准画红色)
struct Edge_Draw_
{
std::string strEdge; // left/right/up/down
float fDist; // 实测距离(原图像素)
float fStandard; // 标准距离(原图像素)
bool bExceed; // |fDist-fStandard| 超限差(x_offset) -> 红色
Edge_Draw_()
{
fDist = 0.0f;
fStandard = 0.0f;
bExceed = false;
}
};
bool bValid; // 本次是否有效计算出支架偏移几何
bool bOffset; // 是否判偏移 NG(十字颜色:红=NG)
cv::Rect rpRect; // 产品区域外接矩形(原图坐标)
cv::Rect rsRect; // 支架区域外接矩形(原图坐标)
cv::Point2f ptCenter; // 支架中心(原图坐标)
std::vector<Edge_Draw_> edgeList; // 各参与判定方向的绘制信息
Support_Draw_Info_()
{
Reset();
}
void Reset()
{
bValid = false;
bOffset = false;
rpRect = cv::Rect(0, 0, 0, 0);
rsRect = cv::Rect(0, 0, 0, 0);
ptCenter = cv::Point2f(0.0f, 0.0f);
edgeList.clear();
}
};
class ImgCheckAnalysisy : public ImgCheckBase class ImgCheckAnalysisy : public ImgCheckBase
{ {
@ -348,6 +387,8 @@ private:
bool m_bHandleDetSucc; // 本次是否完成把手检测 bool m_bHandleDetSucc; // 本次是否完成把手检测
bool m_bHandleFound; // 所有非空把手区域是否都找到了把手 bool m_bHandleFound; // 所有非空把手区域是否都找到了把手
bool m_bHandleExpect; // 是否开启了把手缺失检测(且有非空把手区域) bool m_bHandleExpect; // 是否开启了把手缺失检测(且有非空把手区域)
// 支架偏移检测的绘制信息(CheckRun 支架块填充,DrawResult_Step_1 绘制)
Support_Draw_Info_ m_SupportDrawInfo;
}; };
#endif #endif

@ -892,32 +892,9 @@ int ImageResultJudge::DrawResult(std::shared_ptr<ImageAllResult> pImageResult)
draw_str->txtP = cv::Point(draw_qx->cp.x + r, draw_qx->cp.y - r); draw_str->txtP = cv::Point(draw_qx->cp.x + r, draw_qx->cp.y - r);
draw_str->txtsize = 0.5; draw_str->txtsize = 0.5;
draw_str->txtbackgroundk = txtbackgroundk; draw_str->txtbackgroundk = txtbackgroundk;
cv::Rect srcroi = pqx->srcImgroi;
cv::Point srccp = cv::Point(srcroi.x + srcroi.width * .5, srcroi.y + srcroi.height * 0.5);
char buffer[128];
{
std::string text = std::to_string(pqx->idx) + ":" + pqx->strTypeName;
draw_str->strlist.push_back(text);
}
{
sprintf(buffer, " center:%d %d", srccp.x, srccp.y);
std::string text = buffer;
draw_str->strlist.push_back(text);
}
{
sprintf(buffer, " len %0.2f A: %0.2f ", pqx->len, pqx->area);
std::string text = buffer;
draw_str->strlist.push_back(text);
}
{
sprintf(buffer, " breadth %0.2f A: %0.2f ", pqx->breadth, pqx->area);
std::string text = buffer;
draw_str->strlist.push_back(text);
}
{ {
sprintf(buffer, " E:%0.2f hj %0.2f", pqx->energy, pqx->hj); // 只绘制 id:缺陷code (缺陷名可能中文乱码, 用code替代)
std::string text = buffer; std::string text = std::to_string(pqx->idx) + ":" + pqx->qx_Code;
draw_str->strlist.push_back(text); draw_str->strlist.push_back(text);
} }
sendTask(draw_str); sendTask(draw_str);

@ -891,6 +891,7 @@ int ImgCheckAnalysisy::CheckRun()
4、计算值与标准值(x_standard/y_standard)做差取平均,平均差超过 offset(x_offset) → 偏移 */ 4、计算值与标准值(x_standard/y_standard)做差取平均,平均差超过 offset(x_offset) → 偏移 */
{ {
Function_Support_Det &supportDet = m_pbaseCheckFunction->supportDet; Function_Support_Det &supportDet = m_pbaseCheckFunction->supportDet;
m_SupportDrawInfo.Reset(); // 清空本帧绘制信息(未开启/计算无效时不绘制)
if (supportDet.bDetSupport) if (supportDet.bDetSupport)
{ {
const cv::Mat &bigmask = m_pEdge_Align_Result->bigmask; const cv::Mat &bigmask = m_pEdge_Align_Result->bigmask;
@ -1008,6 +1009,15 @@ int ImgCheckAnalysisy::CheckRun()
float fDiff = fabs(fDist - fStandard); float fDiff = fabs(fDist - fStandard);
fSumDiff += fDiff; fSumDiff += fDiff;
nDiffNum++; nDiffNum++;
// 记录该方向绘制信息:|实测-标准|超限差(x_offset)则绘制为红色
{
Support_Draw_Info_::Edge_Draw_ edgeDraw;
edgeDraw.strEdge = strEdge;
edgeDraw.fDist = fDist;
edgeDraw.fStandard = fStandard;
edgeDraw.bExceed = (fDiff > supportDet.x_offset);
m_SupportDrawInfo.edgeList.push_back(edgeDraw);
}
sprintf(strTmp, "%s(%.1f/%.1f) ", strEdge.c_str(), fDist, fStandard); sprintf(strTmp, "%s(%.1f/%.1f) ", strEdge.c_str(), fDist, fStandard);
strSelect += strTmp; strSelect += strTmp;
sprintf(strTmp, "%.1f ", fDiff); sprintf(strTmp, "%.1f ", fDiff);
@ -1017,6 +1027,13 @@ int ImgCheckAnalysisy::CheckRun()
float fAvgDiff = (nDiffNum > 0) ? (fSumDiff / nDiffNum) : 0.0f; float fAvgDiff = (nDiffNum > 0) ? (fSumDiff / nDiffNum) : 0.0f;
bool bOffset = (nDiffNum > 0) && (fAvgDiff > supportDet.x_offset); bool bOffset = (nDiffNum > 0) && (fAvgDiff > supportDet.x_offset);
// 记录绘制信息(DrawResult_Step_1 用):支架中心十字 + 各方向距离
m_SupportDrawInfo.bValid = true;
m_SupportDrawInfo.bOffset = bOffset;
m_SupportDrawInfo.rpRect = rpRect;
m_SupportDrawInfo.rsRect = rsRect;
m_SupportDrawInfo.ptCenter = cv::Point2f(fCx, fCy);
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", 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", "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, rpRect.x, rpRect.y, rpRect.width, rpRect.height,
@ -2343,21 +2360,7 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
float fs_resize_y = m_pImageAllResult->resultImg.rows * 1.0f / m_pImageAllResult->detImg.rows; float fs_resize_y = m_pImageAllResult->resultImg.rows * 1.0f / m_pImageAllResult->detImg.rows;
// 绘制m_inner_rroi(蓝色),原图坐标 -> 检测图(裁剪) -> 结果图 // 注:不再绘制 inner 定位蓝色框(m_inner_rroi 为大轮廓裁剪子图局部坐标,直接映射会缺裁剪原点偏移)
{
cv::Point2f vertices[4];
m_inner_rroi.points(vertices);
std::vector<cv::Point> draw_pts;
draw_pts.reserve(4);
for (int i = 0; i < 4; i++)
{
cv::Point2f detPt = MatAffinePoint(m_matSrc2Det, vertices[i]);
draw_pts.emplace_back(cvRound(detPt.x * fs_resize_x),
cvRound(detPt.y * fs_resize_y));
}
cv::polylines(m_CheckResult_shareP->resultimg, draw_pts, true,
CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, cv::Scalar(255, 0, 0)), 1);
}
if (m_pbaseCheckFunction && m_pbaseCheckFunction->markLine.region.size() > 1) if (m_pbaseCheckFunction && m_pbaseCheckFunction->markLine.region.size() > 1)
{ {
@ -2400,6 +2403,63 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
} }
} }
// 支架偏移检测绘制:支架中心十字(绿=正常/红=偏移NG)+ 支架到边缘的距离(绿=正常/红=超标准)
if (m_SupportDrawInfo.bValid && !m_CheckResult_shareP->resultimg.empty())
{
// 支架中心:原图坐标 -> 检测图(裁剪) -> 结果图
cv::Point2f detPt = MatAffinePoint(m_matSrc2Det, m_SupportDrawInfo.ptCenter);
cv::Point pCenter(cvRound(detPt.x * fs_resize_x), cvRound(detPt.y * fs_resize_y));
// 支架中心十字:NG 红色 / 正常绿色
cv::Scalar colorCross = m_SupportDrawInfo.bOffset ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 255, 0);
colorCross = CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, colorCross);
const int nCrossLen = 40; // 结果图上十字半长(px)
cv::line(m_CheckResult_shareP->resultimg, cv::Point(pCenter.x - nCrossLen, pCenter.y), cv::Point(pCenter.x + nCrossLen, pCenter.y), colorCross, 2);
cv::line(m_CheckResult_shareP->resultimg, cv::Point(pCenter.x, pCenter.y - nCrossLen), cv::Point(pCenter.x, pCenter.y + nCrossLen), colorCross, 2);
// 支架到边缘的距离:中心 -> 产品区域对应边缘连线 + 实测/标准(像素)文字
char strTxt[64];
for (size_t i = 0; i < m_SupportDrawInfo.edgeList.size(); i++)
{
const Support_Draw_Info_::Edge_Draw_ &edgeDraw = m_SupportDrawInfo.edgeList[i];
cv::Point2f ptEdge = m_SupportDrawInfo.ptCenter;
if (edgeDraw.strEdge == "left")
{
ptEdge.x = (float)m_SupportDrawInfo.rpRect.x;
}
else if (edgeDraw.strEdge == "right")
{
ptEdge.x = (float)(m_SupportDrawInfo.rpRect.x + m_SupportDrawInfo.rpRect.width);
}
else if (edgeDraw.strEdge == "up")
{
ptEdge.y = (float)m_SupportDrawInfo.rpRect.y;
}
else if (edgeDraw.strEdge == "down")
{
ptEdge.y = (float)(m_SupportDrawInfo.rpRect.y + m_SupportDrawInfo.rpRect.height);
}
else
{
continue;
}
cv::Point2f detEdge = MatAffinePoint(m_matSrc2Det, ptEdge);
cv::Point pEdge(cvRound(detEdge.x * fs_resize_x), cvRound(detEdge.y * fs_resize_y));
// 超标准红色 / 正常绿色
cv::Scalar colorEdge = edgeDraw.bExceed ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 255, 0);
colorEdge = CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, colorEdge);
cv::line(m_CheckResult_shareP->resultimg, pCenter, pEdge, colorEdge, 2);
// 距离文字:实测/标准(原图像素)
snprintf(strTxt, sizeof(strTxt), "%s %.0f/%.0f", edgeDraw.strEdge.c_str(), edgeDraw.fDist, edgeDraw.fStandard);
cv::putText(m_CheckResult_shareP->resultimg, strTxt,
cv::Point((pCenter.x + pEdge.x) / 2 + 6, (pCenter.y + pEdge.y) / 2 - 6),
cv::FONT_HERSHEY_SIMPLEX, 0.8, colorEdge, 2);
}
}
// cv::imwrite("sss.png", m_CheckResult_shareP->resultimg); // cv::imwrite("sss.png", m_CheckResult_shareP->resultimg);
return 0; return 0;
} }

Loading…
Cancel
Save