|
|
|
|
@ -22,33 +22,12 @@
|
|
|
|
|
// 待分类模型部署后,将 AI_CLASS_MODEL_ENABLE 置 1,即可恢复 AI_QX_Class_Thread 中的AI分类流程
|
|
|
|
|
#define AI_CLASS_MODEL_ENABLE 0
|
|
|
|
|
|
|
|
|
|
// 把手提取/判定参数(下面宏是“未配置 JSON 时”的默认值;现场可在参数 JSON 的 Handle_Detect.form.handle_param
|
|
|
|
|
// 下按注释标注的键名配置同名参数覆盖它们,按产品/现场情况调整,无需改代码重编译):
|
|
|
|
|
// 提取:用 outer 定位 mask(bigmask) 拟合主体(圆角矩形)的上/下/左/右四条边缘,把边缘外的凸起按连通域分离为把手候选
|
|
|
|
|
// (见 CheckUtil::GetMaskProtrusions;产品主体是圆角矩形、把手基本出现在四角附近)
|
|
|
|
|
// 判定:每个非空的"把手区域"附近都应该有一个把手:凸起宽高与该区域外接矩形宽高的比值落在下面范围内才算匹配
|
|
|
|
|
// 调整影响:
|
|
|
|
|
// HANDLE_SIZE_MIN/MAX_RATIO(JSON: handle_size_min / handle_size_max):凸起与参数把手区域外接矩形的宽高比允许区间
|
|
|
|
|
// 放宽(如 0.7~1.4):把手尺寸/摆放差异大时不易漏检匹配,但可能让邻近错误凸起也匹配上;
|
|
|
|
|
// 收紧(如 0.9~1.1):匹配更严格,把手实际尺寸与参数画得差异大时会误报"把手缺失"
|
|
|
|
|
// 把手判定参数:
|
|
|
|
|
// 每个非空的"把手区域"附近都应该有一个把手:凸起宽高与该区域外接矩形宽高的比值落在下面范围内才算匹配
|
|
|
|
|
#define HANDLE_SIZE_MIN_RATIO 0.8f
|
|
|
|
|
#define HANDLE_SIZE_MAX_RATIO 1.2f
|
|
|
|
|
// "附近"的搜索范围(JSON: handle_search_ratio):把手区域外接矩形在宽/高方向各外扩该比例
|
|
|
|
|
// 调大:位置约束变松,产品摆放偏移大也不易漏(但可能匹配到别的凸起);
|
|
|
|
|
// 调小:位置约束更严,偏移稍大就会判"附近没有把手"
|
|
|
|
|
// "附近"的搜索范围:把手区域外接矩形在宽/高方向各外扩该比例
|
|
|
|
|
#define HANDLE_NEAR_EXPAND_RATIO 5
|
|
|
|
|
// 把手提取:最小深度系数(× 定位模型量化步长)(JSON: handle_min_depth)
|
|
|
|
|
// 凸起区域算大了/过于灵敏(边缘顺边毛刺与把手连成一块、高度多算) → 调大(如 3~4);
|
|
|
|
|
// 浅把手漏检 → 调小(如 1.5)。它同时决定:①凸起深度门槛;②边缘容差带(容差带=最小深度/2)
|
|
|
|
|
#define HANDLE_MIN_DEPTH_STEP_COEF 2
|
|
|
|
|
// 把手提取:最小面积系数(× 最小深度²)(JSON: handle_min_area)。碎块多 → 调大;小把手漏检 → 调小
|
|
|
|
|
#define HANDLE_MIN_AREA_COEF 2
|
|
|
|
|
// 把手提取:边缘外扩量系数(× 量化步长)(JSON: handle_edge_margin,0=按"最小深度一半"自动):
|
|
|
|
|
// 把主体边缘线整体向外推,变相减小凸起区域。
|
|
|
|
|
// 凸起区域算大了(边缘顺边毛刺与把手连成一块、高度多算) → 调大(如 2~3);
|
|
|
|
|
// 浅把手漏检/想更灵敏 → 调小。
|
|
|
|
|
// 注意:凸起区域会同步变小,匹配用的宽高比(HANDLE_SIZE_*_RATIO)会偏小,不宜调得过大
|
|
|
|
|
#define HANDLE_EDGE_MARGIN_STEP_COEF 3
|
|
|
|
|
// 把手缺失检测开关见 Base层 Function_Support_Det::bDetHandle(CheckConfigDefine.h)
|
|
|
|
|
|
|
|
|
|
// 分类映射配置文件(每行:label索引:缺陷名称;冒号后名称为该缺陷类型名称,行的顺序即缺陷类型索引)
|
|
|
|
|
@ -876,9 +855,7 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
m_pdetlog->bPrintStr = true;
|
|
|
|
|
}
|
|
|
|
|
// m_pdetlog->bPrintStr = true;
|
|
|
|
|
m_fImgage_Scale_X = m_pBasicConfig->fImage_Scale_x;
|
|
|
|
|
m_fImgage_Scale_Y = m_pBasicConfig->fImage_Scale_y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "1、basic Info", "---------------------------1、basic Info---------------------------------");
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Version", "%s", GetVersion().c_str());
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Updateconfig", "%d", m_bupdateconfig);
|
|
|
|
|
@ -1070,11 +1047,9 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
|
|
|
|
|
/* 检测支架偏移(bDetSupport 开关关闭时不做支架判定)
|
|
|
|
|
不使用参数绘制区域,结合两次定位的 mask 计算:
|
|
|
|
|
1、产品边缘:优先复用把手提取对 bigmask 做的"四边抗凸起拟合"主体四边形
|
|
|
|
|
(CheckHandleByBigMask -> GetMaskProtrusions;边缘上的凸起(把手等)不会把边缘带偏),
|
|
|
|
|
拟合不可用/无效时回退 bigmask 最大连通域外接矩形 rp(近似矩形,四周可能有凸起)
|
|
|
|
|
1、bigmask 最大连通域 = 产品区域 rp(近似矩形,四周可能有凸起)
|
|
|
|
|
2、smallmask 最大连通域 = 支架区域 rs(按定位裁剪区 m_outer_rroi 外接矩形映射回原图坐标)
|
|
|
|
|
3、按 edge_select 指定方向(left/right/up/down)计算 rs 中心到产品对应边缘的距离
|
|
|
|
|
3、按 edge_select 指定方向(left/right/up/down)计算 rs 中心到 rp 对应边缘的距离
|
|
|
|
|
4、计算值与标准值(x_standard/y_standard)做差取平均,平均差超过 offset(x_offset) → 偏移 */
|
|
|
|
|
{
|
|
|
|
|
Function_Support_Det &supportDet = m_pbaseCheckFunction->supportDet;
|
|
|
|
|
@ -1134,7 +1109,7 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// rp 边缘(拟合不可用时的回退):产品最大连通域的外接矩形(含凸起)
|
|
|
|
|
// rp 边缘:产品最大连通域的外接矩形(含凸起)
|
|
|
|
|
cv::Rect rpRect = cv::boundingRect(rpContour);
|
|
|
|
|
|
|
|
|
|
// rs 映射回原图坐标:smallmask 对应定位时裁剪的大轮廓区域(m_outer_rroi 外接矩形),按比例映射
|
|
|
|
|
@ -1148,87 +1123,13 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
cvRound(rsSmallRect.width * fx),
|
|
|
|
|
cvRound(rsSmallRect.height * fy));
|
|
|
|
|
|
|
|
|
|
// 3、产品边缘:优先复用把手提取对 bigmask 做的"四边抗凸起拟合"主体四边形
|
|
|
|
|
// (CheckHandleByBigMask -> GetMaskProtrusions 输出):边缘上的凸起(把手等)不会把边缘带偏;
|
|
|
|
|
// 拟合不可用/无效时回退到产品最大连通域外接矩形 rpRect(含凸起)
|
|
|
|
|
bool bFitQuad = false; // 是否用上了拟合主体四边形
|
|
|
|
|
float fDL = 0, fDR = 0, fDT = 0, fDB = 0; // 支架中心到产品左/右/上/下边缘的距离(原图像素,后乘缩放系数)
|
|
|
|
|
cv::Point2f ptEdgeArr[4]; // 连线端点(边缘线上):0 left 1 right 2 up 3 down
|
|
|
|
|
cv::Point2f ptSegAArr[4]; // 边缘线段端点A
|
|
|
|
|
cv::Point2f ptSegBArr[4]; // 边缘线段端点B
|
|
|
|
|
|
|
|
|
|
// 3、rs 中心到 rp 上下左右边缘的距离
|
|
|
|
|
float fCx = rsRect.x + rsRect.width * 0.5f;
|
|
|
|
|
float fCy = rsRect.y + rsRect.height * 0.5f;
|
|
|
|
|
if (m_HandleBodyQuad.size() == 4)
|
|
|
|
|
{
|
|
|
|
|
// 四边形角点顺序:左上/右上/右下/左下;左/右边 x=k*y+b、上/下边 y=k*x+b(与 GetMaskProtrusions 约定一致);
|
|
|
|
|
// 边长过小说明拟合退化,不用
|
|
|
|
|
const cv::Point2f &ptTL = m_HandleBodyQuad[0];
|
|
|
|
|
const cv::Point2f &ptTR = m_HandleBodyQuad[1];
|
|
|
|
|
const cv::Point2f &ptBR = m_HandleBodyQuad[2];
|
|
|
|
|
const cv::Point2f &ptBL = m_HandleBodyQuad[3];
|
|
|
|
|
if (fabs(ptBL.y - ptTL.y) > 2.0f && fabs(ptBR.y - ptTR.y) > 2.0f &&
|
|
|
|
|
fabs(ptTR.x - ptTL.x) > 2.0f && fabs(ptBR.x - ptBL.x) > 2.0f)
|
|
|
|
|
{
|
|
|
|
|
float fKL = (ptBL.x - ptTL.x) / (ptBL.y - ptTL.y); // 左边 x = k*y + b
|
|
|
|
|
float fBL = ptTL.x - fKL * ptTL.y;
|
|
|
|
|
float fKR = (ptBR.x - ptTR.x) / (ptBR.y - ptTR.y); // 右边 x = k*y + b
|
|
|
|
|
float fBR = ptTR.x - fKR * ptTR.y;
|
|
|
|
|
float fKT = (ptTR.y - ptTL.y) / (ptTR.x - ptTL.x); // 上边 y = k*x + b
|
|
|
|
|
float fBT = ptTL.y - fKT * ptTL.x;
|
|
|
|
|
float fKB = (ptBR.y - ptBL.y) / (ptBR.x - ptBL.x); // 下边 y = k*x + b
|
|
|
|
|
float fBB = ptBL.y - fKB * ptBL.x;
|
|
|
|
|
// 连线端点:取边缘线在支架中心行/列上的点;长度即中心到边缘的水平/垂直距离
|
|
|
|
|
float fXEdgeL = fKL * fCy + fBL;
|
|
|
|
|
float fXEdgeR = fKR * fCy + fBR;
|
|
|
|
|
float fYEdgeT = fKT * fCx + fBT;
|
|
|
|
|
float fYEdgeB = fKB * fCx + fBB;
|
|
|
|
|
fDL = fCx - fXEdgeL;
|
|
|
|
|
fDR = fXEdgeR - fCx;
|
|
|
|
|
fDT = fCy - fYEdgeT;
|
|
|
|
|
fDB = fYEdgeB - fCy;
|
|
|
|
|
// 拟合有效性:支架中心应位于四条拟合边内侧(四个方向距离均为正),否则放弃拟合
|
|
|
|
|
if (fDL > 0 && fDR > 0 && fDT > 0 && fDB > 0)
|
|
|
|
|
{
|
|
|
|
|
ptEdgeArr[0] = cv::Point2f(fXEdgeL, fCy);
|
|
|
|
|
ptEdgeArr[1] = cv::Point2f(fXEdgeR, fCy);
|
|
|
|
|
ptEdgeArr[2] = cv::Point2f(fCx, fYEdgeT);
|
|
|
|
|
ptEdgeArr[3] = cv::Point2f(fCx, fYEdgeB);
|
|
|
|
|
ptSegAArr[0] = ptTL; ptSegBArr[0] = ptBL; // left:整条拟合左边
|
|
|
|
|
ptSegAArr[1] = ptTR; ptSegBArr[1] = ptBR; // right:整条拟合右边
|
|
|
|
|
ptSegAArr[2] = ptTL; ptSegBArr[2] = ptTR; // up:整条拟合上边
|
|
|
|
|
ptSegAArr[3] = ptBL; ptSegBArr[3] = ptBR; // down:整条拟合下边
|
|
|
|
|
bFitQuad = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!bFitQuad)
|
|
|
|
|
{
|
|
|
|
|
// 回退:产品区域外接矩形(含凸起)的四条边
|
|
|
|
|
fDL = fCx - rpRect.x;
|
|
|
|
|
fDR = (rpRect.x + rpRect.width) - fCx;
|
|
|
|
|
fDT = fCy - rpRect.y;
|
|
|
|
|
fDB = (rpRect.y + rpRect.height) - fCy;
|
|
|
|
|
ptEdgeArr[0] = cv::Point2f((float)rpRect.x, fCy);
|
|
|
|
|
ptEdgeArr[1] = cv::Point2f((float)(rpRect.x + rpRect.width), fCy);
|
|
|
|
|
ptEdgeArr[2] = cv::Point2f(fCx, (float)rpRect.y);
|
|
|
|
|
ptEdgeArr[3] = cv::Point2f(fCx, (float)(rpRect.y + rpRect.height));
|
|
|
|
|
ptSegAArr[0] = cv::Point2f((float)rpRect.x, (float)rpRect.y);
|
|
|
|
|
ptSegBArr[0] = cv::Point2f((float)rpRect.x, (float)(rpRect.y + rpRect.height));
|
|
|
|
|
ptSegAArr[1] = cv::Point2f((float)(rpRect.x + rpRect.width), (float)rpRect.y);
|
|
|
|
|
ptSegBArr[1] = cv::Point2f((float)(rpRect.x + rpRect.width), (float)(rpRect.y + rpRect.height));
|
|
|
|
|
ptSegAArr[2] = cv::Point2f((float)rpRect.x, (float)rpRect.y);
|
|
|
|
|
ptSegBArr[2] = cv::Point2f((float)(rpRect.x + rpRect.width), (float)rpRect.y);
|
|
|
|
|
ptSegAArr[3] = cv::Point2f((float)rpRect.x, (float)(rpRect.y + rpRect.height));
|
|
|
|
|
ptSegBArr[3] = cv::Point2f((float)(rpRect.x + rpRect.width), (float)(rpRect.y + rpRect.height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 像素 -> mm
|
|
|
|
|
fDL *= m_fImgage_Scale_X;
|
|
|
|
|
fDR *= m_fImgage_Scale_X;
|
|
|
|
|
fDT *= m_fImgage_Scale_Y;
|
|
|
|
|
fDB *= m_fImgage_Scale_Y;
|
|
|
|
|
float fDistArr[4] = {fDL, fDR, fDT, fDB}; // 顺序固定:0 left 1 right 2 up 3 down
|
|
|
|
|
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
|
|
|
|
|
@ -1240,26 +1141,26 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
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
|
|
|
|
|
float fDist = 0;
|
|
|
|
|
float fStandard = 0;
|
|
|
|
|
if (strEdge == "left")
|
|
|
|
|
{
|
|
|
|
|
gi = 0;
|
|
|
|
|
fDist = fDL;
|
|
|
|
|
fStandard = supportDet.x_standard;
|
|
|
|
|
}
|
|
|
|
|
else if (strEdge == "right")
|
|
|
|
|
{
|
|
|
|
|
gi = 1;
|
|
|
|
|
fDist = fDR;
|
|
|
|
|
fStandard = supportDet.x_standard;
|
|
|
|
|
}
|
|
|
|
|
else if (strEdge == "up")
|
|
|
|
|
{
|
|
|
|
|
gi = 2;
|
|
|
|
|
fDist = fDT;
|
|
|
|
|
fStandard = supportDet.y_standard;
|
|
|
|
|
}
|
|
|
|
|
else if (strEdge == "down")
|
|
|
|
|
{
|
|
|
|
|
gi = 3;
|
|
|
|
|
fDist = fDB;
|
|
|
|
|
fStandard = supportDet.y_standard;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
@ -1267,7 +1168,6 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float fDist = fDistArr[gi];
|
|
|
|
|
float fDiff = fabs(fDist - fStandard);
|
|
|
|
|
fSumDiff += fDiff;
|
|
|
|
|
nDiffNum++;
|
|
|
|
|
@ -1278,9 +1178,6 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
edgeDraw.fDist = fDist;
|
|
|
|
|
edgeDraw.fStandard = fStandard;
|
|
|
|
|
edgeDraw.bExceed = (fDiff > supportDet.x_offset);
|
|
|
|
|
edgeDraw.ptEdge = ptEdgeArr[gi];
|
|
|
|
|
edgeDraw.ptEdgeA = ptSegAArr[gi];
|
|
|
|
|
edgeDraw.ptEdgeB = ptSegBArr[gi];
|
|
|
|
|
m_SupportDrawInfo.edgeList.push_back(edgeDraw);
|
|
|
|
|
}
|
|
|
|
|
sprintf(strTmp, "%s(%.1f/%.1f) ", strEdge.c_str(), fDist, fStandard);
|
|
|
|
|
@ -1300,10 +1197,10 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
m_SupportDrawInfo.ptCenter = cv::Point2f(fCx, fCy);
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测",
|
|
|
|
|
"rp [%d %d %d %d] rs [%d %d %d %d] scale (%.4f %.4f) center (%.1f %.1f) edge %s quad %ld 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,
|
|
|
|
|
rsRect.x, rsRect.y, rsRect.width, rsRect.height,
|
|
|
|
|
fx, fy, fCx, fCy, bFitQuad ? "fitquad" : "rpbox", (long)m_HandleBodyQuad.size(), fDL, fDR, fDT, fDB,
|
|
|
|
|
fx, fy, fCx, fCy, fDL, fDR, fDT, fDB,
|
|
|
|
|
strSelect.c_str(), strDiff.c_str(), fAvgDiff, supportDet.x_offset, bOffset);
|
|
|
|
|
|
|
|
|
|
if (bOffset)
|
|
|
|
|
@ -1419,35 +1316,10 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
{
|
|
|
|
|
m_tag_roiList.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 标签能量/灰阶差(hj)计算用掩膜:所有标签区域填白(与 blob 检测用 AIMaskImg 的口径一致),
|
|
|
|
|
// 由 calc_blob_info_withstats 按“前景=标签区域、背景=外扩一圈”计算
|
|
|
|
|
long tagEnergy_s = CheckUtil::getcurTime();
|
|
|
|
|
cv::Mat tagMask;
|
|
|
|
|
if (!m_tag_roiList.empty() && !m_pImageAllResult->detImg.empty())
|
|
|
|
|
{
|
|
|
|
|
tagMask = cv::Mat::zeros(m_pImageAllResult->detImg.size(), CV_8UC1);
|
|
|
|
|
std::vector<std::vector<cv::Point>> tagPolys;
|
|
|
|
|
tagPolys.reserve(m_tag_roiList.size());
|
|
|
|
|
for (const auto &tagRoiMask : m_tag_roiList)
|
|
|
|
|
{
|
|
|
|
|
cv::Point2f ptfMask[4];
|
|
|
|
|
tagRoiMask.points(ptfMask);
|
|
|
|
|
std::vector<cv::Point> polyMask(4);
|
|
|
|
|
for (int k = 0; k < 4; k++)
|
|
|
|
|
{
|
|
|
|
|
polyMask[k] = cv::Point(cvRound(ptfMask[k].x), cvRound(ptfMask[k].y));
|
|
|
|
|
}
|
|
|
|
|
tagPolys.push_back(std::move(polyMask));
|
|
|
|
|
}
|
|
|
|
|
cv::fillPoly(tagMask, tagPolys, cv::Scalar(255));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto &tagRoi : m_tag_roiList)
|
|
|
|
|
{
|
|
|
|
|
// tag roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系
|
|
|
|
|
// cv::Rect roi = MatAffineRect(m_matSrc2Det, tagRoi.boundingRect());
|
|
|
|
|
cv::Rect roi = tagRoi.boundingRect();
|
|
|
|
|
cv::Rect roi = MatAffineRect(m_matSrc2Det, tagRoi.boundingRect());
|
|
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
|
|
if (roi.width <= 0 || roi.height <= 0)
|
|
|
|
|
{
|
|
|
|
|
@ -1464,16 +1336,6 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
float th = tagRoi.size.height;
|
|
|
|
|
temerror.flen = (tw > th ? tw : th) * m_fImgage_Scale_X;
|
|
|
|
|
temerror.fbreadth = (tw > th ? th : tw) * m_fImgage_Scale_Y;
|
|
|
|
|
|
|
|
|
|
// 标签区域的能量/灰阶差(hj):与 blob 检测相同口径(前景=标签区域,背景=外扩一圈)
|
|
|
|
|
if (!tagMask.empty())
|
|
|
|
|
{
|
|
|
|
|
cv::Rect roiStat = roi;
|
|
|
|
|
cv::Scalar tagStat = calc_blob_info_withstats(m_pImageAllResult->detImg, tagMask, roiStat);
|
|
|
|
|
temerror.energy = (int)tagStat[1];
|
|
|
|
|
temerror.grayDis = (float)tagStat[2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_cell_TBBQ;
|
|
|
|
|
temerror.qx_name = CONFIG_QX_NAME_Names[CONFIG_QX_NAME_cell_TBBQ];
|
|
|
|
|
temerror.result = QX_RESULT_TYPE_NG;
|
|
|
|
|
@ -1498,13 +1360,7 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Tag检测", "tag %d roi [%d %d %d %d] energy %d hj %.1f -> NG",
|
|
|
|
|
temerror.Idx, roi.x, roi.y, roi.width, roi.height, temerror.energy, temerror.grayDis);
|
|
|
|
|
}
|
|
|
|
|
if (!m_tag_roiList.empty())
|
|
|
|
|
{
|
|
|
|
|
long tagEnergy_e = CheckUtil::getcurTime();
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Tag", "tag energy calc use time %ld", tagEnergy_e - tagEnergy_s);
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Tag检测", "tag %d roi [%d %d %d %d] -> NG", temerror.Idx, roi.x, roi.y, roi.width, roi.height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -2256,8 +2112,6 @@ int ImgCheckAnalysisy::ConfigCheck(cv::Mat img)
|
|
|
|
|
|
|
|
|
|
int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg)
|
|
|
|
|
{
|
|
|
|
|
std::string cur_time_s = CheckUtil::getCurTimeHMS();
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<AIModel_Base> pAIDet;
|
|
|
|
|
// printf("=====>>>>AI_Detect_Thread m_strCurDetChannel %s \n", m_strCurDetChannel.c_str());
|
|
|
|
|
bool bUseDarkModel = false;
|
|
|
|
|
@ -2367,12 +2221,12 @@ int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg)
|
|
|
|
|
|
|
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveAIDetImg && !ResultImg.empty())
|
|
|
|
|
{
|
|
|
|
|
std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + DetImgInfo_shareP->strImgProductID + "/";
|
|
|
|
|
std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/KK/" + DetImgInfo_shareP->strImgProductID + "/";
|
|
|
|
|
CheckUtil::CreateDir(strSaveDir);
|
|
|
|
|
std::string strSavePath_in = strSaveDir + cur_time_s + "_" + strCameraName + "_in.png";
|
|
|
|
|
std::string strSavePath_out = strSaveDir + cur_time_s + "_" + strCameraName + "_out.png";
|
|
|
|
|
CheckUtil::SaveImgAsync(strSavePath_in, m_CheckResult_shareP->cutSrcimg);
|
|
|
|
|
CheckUtil::SaveImgAsync(strSavePath_out, ResultImg);
|
|
|
|
|
std::string strSavePath_in = strSaveDir + m_strCurDetChannel + "_in.png";
|
|
|
|
|
std::string strSavePath_out = strSaveDir + m_strCurDetChannel + "_out.png";
|
|
|
|
|
cv::imwrite(strSavePath_in, inputImg);
|
|
|
|
|
cv::imwrite(strSavePath_out, ResultImg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 临时存图
|
|
|
|
|
@ -2693,47 +2547,56 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 支架偏移检测绘制(受参数 bDrawSupport 控制,默认关闭):
|
|
|
|
|
// 支架中心十字(绿=正常/红=偏移NG)+ 各方向"所计算的产品边缘"线段(抗凸起拟合直边,见 CheckRun 支架块)
|
|
|
|
|
// + 支架中心到该边缘的连线(连线长度=支架中心到所计算边缘的距离;绿=正常/红=超标准)+ 实测/标准(mm)文字
|
|
|
|
|
if (m_SupportDrawInfo.bValid && m_pbaseCheckFunction->supportDet.bDrawSupport && !m_CheckResult_shareP->resultimg.empty())
|
|
|
|
|
// 支架偏移检测绘制:支架中心十字(绿=正常/红=偏移NG)+ 支架到边缘的距离(绿=正常/红=超标准)
|
|
|
|
|
if (m_SupportDrawInfo.bValid && !m_CheckResult_shareP->resultimg.empty())
|
|
|
|
|
{
|
|
|
|
|
// 原图坐标 -> 检测图(裁剪) -> 结果图
|
|
|
|
|
auto srcToResult = [&](const cv::Point2f &pt) -> cv::Point {
|
|
|
|
|
cv::Point2f detPt = MatAffinePoint(m_matSrc2Det, pt);
|
|
|
|
|
return cv::Point(cvRound(detPt.x * fs_resize_x), cvRound(detPt.y * fs_resize_y));
|
|
|
|
|
};
|
|
|
|
|
// 支架中心:原图坐标 -> 检测图(裁剪) -> 结果图
|
|
|
|
|
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::Point pCenter = srcToResult(m_SupportDrawInfo.ptCenter);
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
// 各方向:所计算边缘线段(抗凸起拟合直边/回退外接矩形边) + 中心到边缘点的连线(长度=点到所计算边缘) + 实测/标准文字
|
|
|
|
|
// 几何(边缘点/线段两端点)由 CheckRun 支架块算好存入 edgeDraw,此处只做坐标映射与绘制
|
|
|
|
|
// 支架到边缘的距离:中心 -> 产品区域对应边缘连线 + 实测/标准(像素)文字
|
|
|
|
|
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::Point pEdge = srcToResult(edgeDraw.ptEdge);
|
|
|
|
|
cv::Point pEdgeA = srcToResult(edgeDraw.ptEdgeA);
|
|
|
|
|
cv::Point pEdgeB = srcToResult(edgeDraw.ptEdgeB);
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
// 1、所计算边缘线段:标出参与计算的产品边缘(细线;抗凸起拟合直边/回退边)
|
|
|
|
|
cv::line(m_CheckResult_shareP->resultimg, pEdgeA, pEdgeB, colorEdge, 1);
|
|
|
|
|
|
|
|
|
|
// 2、中心 -> 边缘点连线:长度即支架中心到所计算边缘的距离
|
|
|
|
|
cv::line(m_CheckResult_shareP->resultimg, pCenter, pEdge, colorEdge, 2);
|
|
|
|
|
|
|
|
|
|
// 3、距离文字:实测/标准(mm)
|
|
|
|
|
// 距离文字:实测/标准(原图像素)
|
|
|
|
|
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),
|
|
|
|
|
@ -2912,7 +2775,7 @@ int ImgCheckAnalysisy::AI_Edge(const cv::Mat &img, cv::RotatedRect &outerRoi, cv
|
|
|
|
|
// 无条件赋值:Tag_Loc 未检出或 bDetTag 关闭(tagroi 为空)时清空,避免残留上一张图的标签
|
|
|
|
|
tagroiList = m_pEdge_Align_Result->tagroi;
|
|
|
|
|
|
|
|
|
|
/* 用 outer 定位 mask(bigmask) 分离凸起区域,检测把手是否缺失 */
|
|
|
|
|
/*使用m_pEdge_Align_Result->bigmask检测把手是否缺失*/
|
|
|
|
|
{
|
|
|
|
|
long handle_s = CheckUtil::getcurTime();
|
|
|
|
|
CheckHandleByBigMask(m_pEdge_Align_Result->bigmask);
|
|
|
|
|
@ -2922,23 +2785,19 @@ int ImgCheckAnalysisy::AI_Edge(const cv::Mat &img, cv::RotatedRect &outerRoi, cv
|
|
|
|
|
return re;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用 outer 定位 mask(bigmask) 检测把手(产品主体是圆角矩形、把手基本出现在四角附近):
|
|
|
|
|
// 1、分离凸起区域:拟合主体上/下/左/右四条边缘,把边缘外的掩膜按连通域切分成凸起区域
|
|
|
|
|
// (提取细节见 CheckUtil::GetMaskProtrusions,含就近角/质心/面积/深度/填充率)
|
|
|
|
|
// 2、参数里每个非空的"把手区域"附近都应该有一个把手:在区域附近找凸起,
|
|
|
|
|
// 凸起宽高与区域外接矩形宽高相近(尺寸比区间见 handle_size_min/handle_size_max,可 JSON 配置)才算匹配
|
|
|
|
|
// 3、凸起本身不算 NG,只有"某个把手区域附近找不到匹配的凸起"才算把手缺失(NG 由 CheckRun 上报)
|
|
|
|
|
// 4、-s(存过程图)时输出 {通道}_handle_det.jpg:绿色四边形=拟合主体、红框=凸起(带编号/角/深度)、
|
|
|
|
|
// 蓝框=参数把手区域、绿框=匹配到的把手,便于从图像观察问题
|
|
|
|
|
// 5、拟合出的主体四边形存 m_HandleBodyQuad:支架偏移检测复用其四边(抗凸起)做测距与绘制
|
|
|
|
|
// 用 bigmask 检测把手:
|
|
|
|
|
// 1、bigmask 主体近似矩形,把手是主体四周凸起的"类矩形块"
|
|
|
|
|
// 2、把主体四周的凸起抠出来(凸起在该边的边界值上表现为一次突变进入 + 一次反向突变离开)
|
|
|
|
|
// 3、参数里每个非空的"把手区域"附近都应该有一个把手:在区域附近找凸起,
|
|
|
|
|
// 凸起宽高与区域外接矩形宽高相近(HANDLE_SIZE_MIN_RATIO ~ HANDLE_SIZE_MAX_RATIO)才算匹配
|
|
|
|
|
// 4、凸起本身不算 NG,只有"某个把手区域附近找不到匹配的凸起"才算把手缺失(NG 由 CheckRun 上报)
|
|
|
|
|
int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &bigmask)
|
|
|
|
|
{
|
|
|
|
|
m_bHandleDetSucc = false;
|
|
|
|
|
m_bHandleFound = false;
|
|
|
|
|
m_bHandleExpect = false;
|
|
|
|
|
m_HandleProtrusionList.clear();
|
|
|
|
|
m_HandleCandList.clear();
|
|
|
|
|
m_HandleRegionResultList.clear();
|
|
|
|
|
m_HandleBodyQuad.clear();
|
|
|
|
|
|
|
|
|
|
if (bigmask.empty())
|
|
|
|
|
{
|
|
|
|
|
@ -2946,40 +2805,9 @@ int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &bigmask)
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1、从 bigmask 分离凸起区域
|
|
|
|
|
// 最小深度 = 系数 × 量化步长:mask 由模型输出 resize 回原图,直边带量化台阶毛刺(台阶高≈模型 1 像素=量化步长);
|
|
|
|
|
// 系数优先取参数配置(JSON handle_param 下的 handle_min_depth/handle_min_area/handle_edge_margin),
|
|
|
|
|
// 未配置时用文件顶部宏的默认值;
|
|
|
|
|
// 凸起区域算大了/过于灵敏 → 首选调大 handle_edge_margin(把边缘线整体外推、变相减小凸起区域,
|
|
|
|
|
// 不影响深度门槛);也可调大 handle_min_depth(深度门槛+容差带一起变大);浅把手漏检 → 反向调小;
|
|
|
|
|
// 拿不到模型尺寸时传 0,由 GetMaskProtrusions 按掩膜尺寸自动取值
|
|
|
|
|
float fMinDepthCoef = HANDLE_MIN_DEPTH_STEP_COEF;
|
|
|
|
|
float fMinAreaCoef = HANDLE_MIN_AREA_COEF;
|
|
|
|
|
float fEdgeMarginCoef = HANDLE_EDGE_MARGIN_STEP_COEF;
|
|
|
|
|
if (m_pbaseCheckFunction != NULL)
|
|
|
|
|
{
|
|
|
|
|
const Function_Support_Det &supportDetCfg = m_pbaseCheckFunction->supportDet;
|
|
|
|
|
if (supportDetCfg.handle_min_depth_coef > 0) fMinDepthCoef = supportDetCfg.handle_min_depth_coef;
|
|
|
|
|
if (supportDetCfg.handle_min_area_coef > 0) fMinAreaCoef = supportDetCfg.handle_min_area_coef;
|
|
|
|
|
if (supportDetCfg.handle_edge_margin_coef >= 0) fEdgeMarginCoef = supportDetCfg.handle_edge_margin_coef; // 0=按“最小深度一半”自动
|
|
|
|
|
}
|
|
|
|
|
int nMinDepth = 0;
|
|
|
|
|
int nMinArea = 0;
|
|
|
|
|
int nEdgeMargin = 0;
|
|
|
|
|
if (AI_Factory && AI_Factory->Align_Outer && AI_Factory->Align_Outer->input_0.width > 0 && AI_Factory->Align_Outer->input_0.height > 0)
|
|
|
|
|
{
|
|
|
|
|
int nStepX = (int)std::ceil(bigmask.cols * 1.0 / AI_Factory->Align_Outer->input_0.width);
|
|
|
|
|
int nStepY = (int)std::ceil(bigmask.rows * 1.0 / AI_Factory->Align_Outer->input_0.height);
|
|
|
|
|
int nStep = std::max(1, std::max(nStepX, nStepY));
|
|
|
|
|
nMinDepth = (int)(fMinDepthCoef * nStep + 0.5f);
|
|
|
|
|
nMinArea = (int)(fMinAreaCoef * nMinDepth * nMinDepth + 0.5f);
|
|
|
|
|
if (fEdgeMarginCoef > 0)
|
|
|
|
|
{
|
|
|
|
|
nEdgeMargin = (int)(fEdgeMarginCoef * nStep + 0.5f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// m_HandleBodyQuad:拟合出的主体四边形(左上/右上/右下/左下);-s 存图观察用,支架偏移检测复用其四边做抗凸起测距/绘制
|
|
|
|
|
int re = CheckUtil::GetMaskProtrusions(bigmask, m_HandleProtrusionList, nMinDepth, nMinArea, &m_HandleBodyQuad, nEdgeMargin);
|
|
|
|
|
// 1、抠出 bigmask 四周凸起的矩形块
|
|
|
|
|
std::vector<Mask_Protrusion_> protrusionList;
|
|
|
|
|
int re = CheckUtil::GetMaskProtrusions(bigmask, protrusionList);
|
|
|
|
|
if (re != 0)
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "GetMaskProtrusions error %d", re);
|
|
|
|
|
@ -3001,27 +2829,33 @@ int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &bigmask)
|
|
|
|
|
}
|
|
|
|
|
m_bHandleExpect = (bDetHandle && !regionBoxList.empty());
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "protrusion num %ld handle region num %ld bDetHandle %s expect %s",
|
|
|
|
|
m_HandleProtrusionList.size(), regionBoxList.size(), BOOL_TO_STR(bDetHandle), BOOL_TO_STR(m_bHandleExpect));
|
|
|
|
|
|
|
|
|
|
// 3、凸起区域实测特征(就近角/面积/深度/填充率,用于日志排查)
|
|
|
|
|
for (size_t i = 0; i < m_HandleProtrusionList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
const Mask_Protrusion_ &protr = m_HandleProtrusionList[i];
|
|
|
|
|
protrusionList.size(), regionBoxList.size(), BOOL_TO_STR(bDetHandle), BOOL_TO_STR(m_bHandleExpect));
|
|
|
|
|
|
|
|
|
|
// 3、凸起候选实测特征(面积/填充率,仅用于日志排查)
|
|
|
|
|
for (size_t i = 0; i < protrusionList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
Handle_Candidate_ cand;
|
|
|
|
|
cand.protr = protrusionList[i];
|
|
|
|
|
// 左/右凸起:沿边为 y 方向,深度为 x 方向;上/下凸起相反
|
|
|
|
|
bool bHorz = (cand.protr.nSide == 0 || cand.protr.nSide == 1);
|
|
|
|
|
cand.nLen = bHorz ? cand.protr.roi.height : cand.protr.roi.width;
|
|
|
|
|
cand.nDepth = bHorz ? cand.protr.roi.width : cand.protr.roi.height;
|
|
|
|
|
m_HandleCandList.push_back(cand);
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle",
|
|
|
|
|
"protrusion %ld corner %d roi [%d %d %d %d] len %.0f depth %.0f area %0.0f fill %0.2f",
|
|
|
|
|
i, protr.nCorner, protr.roi.x, protr.roi.y, protr.roi.width, protr.roi.height,
|
|
|
|
|
protr.fLen, protr.fDepth, protr.fArea, protr.fFillRatio);
|
|
|
|
|
"protrusion %ld side %d roi [%d %d %d %d] len %d depth %d area %0.0f fill %0.2f",
|
|
|
|
|
i, cand.protr.nSide, cand.protr.roi.x, cand.protr.roi.y, cand.protr.roi.width, cand.protr.roi.height,
|
|
|
|
|
cand.nLen, cand.nDepth, cand.protr.fArea, cand.protr.fFillRatio);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4、逐个把手区域:在区域附近找宽高匹配的凸起(同一个凸起只匹配给一个区域)
|
|
|
|
|
std::vector<bool> usedList(m_HandleProtrusionList.size(), false);
|
|
|
|
|
std::vector<bool> usedList(m_HandleCandList.size(), false);
|
|
|
|
|
int nFound = 0;
|
|
|
|
|
for (size_t ir = 0; ir < regionBoxList.size(); ir++)
|
|
|
|
|
{
|
|
|
|
|
Handle_Region_Result_ result;
|
|
|
|
|
result.nIdx = (int)ir + 1;
|
|
|
|
|
result.boxRegion = regionBoxList[ir];
|
|
|
|
|
int nMatchIdx = MatchHandleRegion(m_HandleProtrusionList, usedList, result.boxRegion, result.roiHandle);
|
|
|
|
|
int nMatchIdx = MatchHandleRegion(m_HandleCandList, usedList, result.boxRegion, result.roiHandle);
|
|
|
|
|
result.bFound = (nMatchIdx >= 0);
|
|
|
|
|
if (result.bFound)
|
|
|
|
|
{
|
|
|
|
|
@ -3045,9 +2879,7 @@ int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &bigmask)
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, "Handle", "handle region num %ld found num %d -> %s",
|
|
|
|
|
regionBoxList.size(), nFound, m_bHandleFound ? "handle ok" : "handle missing !!");
|
|
|
|
|
|
|
|
|
|
// 5、存过程图(-s 时):用于从图像观察把手提取/匹配情况;
|
|
|
|
|
// 绿色四边形=拟合出的主体四条边缘;红框=分离出的凸起区域(带编号/就近角/深度);
|
|
|
|
|
// 蓝框=参数把手区域;绿框=匹配到的把手
|
|
|
|
|
// 5、存过程图:凸起红框、把手区域蓝框、匹配上的把手绿框(仅调试时)
|
|
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
|
|
{
|
|
|
|
|
cv::Mat show;
|
|
|
|
|
@ -3059,25 +2891,9 @@ int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &bigmask)
|
|
|
|
|
{
|
|
|
|
|
show = bigmask.clone();
|
|
|
|
|
}
|
|
|
|
|
// 主体四边形(拟合出的四条边缘)
|
|
|
|
|
if (m_HandleBodyQuad.size() == 4)
|
|
|
|
|
{
|
|
|
|
|
std::vector<cv::Point> vecQuad;
|
|
|
|
|
for (size_t i = 0; i < m_HandleBodyQuad.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
vecQuad.push_back(cv::Point(cvRound(m_HandleBodyQuad[i].x), cvRound(m_HandleBodyQuad[i].y)));
|
|
|
|
|
}
|
|
|
|
|
cv::polylines(show, vecQuad, true, cv::Scalar(0, 255, 0), 8);
|
|
|
|
|
}
|
|
|
|
|
// 凸起区域:红框 + 编号/就近角/深度
|
|
|
|
|
char strTxt[128];
|
|
|
|
|
for (size_t i = 0; i < m_HandleProtrusionList.size(); i++)
|
|
|
|
|
for (size_t i = 0; i < m_HandleCandList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
const Mask_Protrusion_ &protr = m_HandleProtrusionList[i];
|
|
|
|
|
cv::rectangle(show, protr.roi, cv::Scalar(0, 0, 255), 8);
|
|
|
|
|
snprintf(strTxt, sizeof(strTxt), "#%d C%d d%.0f", (int)i, protr.nCorner, protr.fDepth);
|
|
|
|
|
cv::putText(show, strTxt, cv::Point(protr.roi.x, std::max(30, protr.roi.y - 8)),
|
|
|
|
|
cv::FONT_HERSHEY_SIMPLEX, 2.0, cv::Scalar(0, 255, 255), 4);
|
|
|
|
|
cv::rectangle(show, m_HandleCandList[i].protr.roi, cv::Scalar(0, 0, 255), 8);
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < m_HandleRegionResultList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
@ -3193,15 +3009,6 @@ int ImgCheckAnalysisy::CheckTagByBigMask(const cv::Mat &img, const cv::Mat &bigm
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveBQDetImg)
|
|
|
|
|
{
|
|
|
|
|
std::string cur_time_s = CheckUtil::getCurTimeHMS();
|
|
|
|
|
std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + DetImgInfo_shareP->strImgProductID + "/";
|
|
|
|
|
CheckUtil::CreateDir(strSaveDir);
|
|
|
|
|
std::string strSavePath_out = strSaveDir + cur_time_s + "_" + DetImgInfo_shareP->strCameraName + "_BQ_out.png";
|
|
|
|
|
CheckUtil::SaveImgAsync(strSavePath_out, mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 调试存图(仅 -s 时保存)
|
|
|
|
|
if (DetImgInfo_shareP && DetImgInfo_shareP->bsaveProcessImg)
|
|
|
|
|
{
|
|
|
|
|
@ -3240,12 +3047,10 @@ int ImgCheckAnalysisy::CheckTagByBigMask(const cv::Mat &img, const cv::Mat &bigm
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 在某个把手区域附近找宽高匹配的凸起
|
|
|
|
|
// 匹配条件:1、凸起中心落在把手区域附近(区域宽/高各外扩 fNearRatio 倍)
|
|
|
|
|
// 2、凸起宽高与该区域外接矩形宽高的比值都在 [fSizeMin, fSizeMax](允许横竖互换)
|
|
|
|
|
// 范围参数优先取参数配置(JSON handle_param 下的 handle_search_ratio/handle_size_min/handle_size_max),
|
|
|
|
|
// 未配置时用文件顶部宏的默认值
|
|
|
|
|
// 匹配条件:1、凸起中心落在把手区域附近(区域宽/高各外扩 HANDLE_NEAR_EXPAND_RATIO)
|
|
|
|
|
// 2、凸起宽高与该区域外接矩形宽高的比值都在 [HANDLE_SIZE_MIN_RATIO, HANDLE_SIZE_MAX_RATIO](允许横竖互换)
|
|
|
|
|
// 返回凸起在 candList 中的下标,-1 表示没找到
|
|
|
|
|
int ImgCheckAnalysisy::MatchHandleRegion(const std::vector<Mask_Protrusion_> &candList, const std::vector<bool> &usedList,
|
|
|
|
|
int ImgCheckAnalysisy::MatchHandleRegion(const std::vector<Handle_Candidate_> &candList, const std::vector<bool> &usedList,
|
|
|
|
|
const cv::Rect &boxRegion, cv::Rect &roiHandle)
|
|
|
|
|
{
|
|
|
|
|
if (boxRegion.width <= 0 || boxRegion.height <= 0)
|
|
|
|
|
@ -3253,21 +3058,9 @@ int ImgCheckAnalysisy::MatchHandleRegion(const std::vector<Mask_Protrusion_> &ca
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 匹配范围参数:JSON 配置优先,未配置时用默认值
|
|
|
|
|
float fNearRatio = HANDLE_NEAR_EXPAND_RATIO;
|
|
|
|
|
float fSizeMin = HANDLE_SIZE_MIN_RATIO;
|
|
|
|
|
float fSizeMax = HANDLE_SIZE_MAX_RATIO;
|
|
|
|
|
if (m_pbaseCheckFunction != NULL)
|
|
|
|
|
{
|
|
|
|
|
const Function_Support_Det &supportDetCfg = m_pbaseCheckFunction->supportDet;
|
|
|
|
|
if (supportDetCfg.handle_search_ratio > 0) fNearRatio = supportDetCfg.handle_search_ratio;
|
|
|
|
|
if (supportDetCfg.handle_size_min_ratio > 0) fSizeMin = supportDetCfg.handle_size_min_ratio;
|
|
|
|
|
if (supportDetCfg.handle_size_max_ratio > 0) fSizeMax = supportDetCfg.handle_size_max_ratio;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 搜索范围:把手区域外扩(宽/高各外扩搜索倍数倍),容忍产品摆放偏差
|
|
|
|
|
int nx = std::max(10, (int)(boxRegion.width * fNearRatio));
|
|
|
|
|
int ny = std::max(10, (int)(boxRegion.height * fNearRatio));
|
|
|
|
|
// 搜索范围:把手区域外扩(宽/高各外扩区域尺寸的一半),容忍产品摆放偏差
|
|
|
|
|
int nx = std::max(10, (int)(boxRegion.width * HANDLE_NEAR_EXPAND_RATIO));
|
|
|
|
|
int ny = std::max(10, (int)(boxRegion.height * HANDLE_NEAR_EXPAND_RATIO));
|
|
|
|
|
cv::Rect searchBox(boxRegion.x - nx, boxRegion.y - ny, boxRegion.width + 2 * nx, boxRegion.height + 2 * ny);
|
|
|
|
|
cv::Point pRegionCenter(boxRegion.x + boxRegion.width / 2, boxRegion.y + boxRegion.height / 2);
|
|
|
|
|
|
|
|
|
|
@ -3281,8 +3074,8 @@ int ImgCheckAnalysisy::MatchHandleRegion(const std::vector<Mask_Protrusion_> &ca
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const Mask_Protrusion_ &cand = candList[i];
|
|
|
|
|
const cv::Rect &roi = cand.roi;
|
|
|
|
|
const Handle_Candidate_ &cand = candList[i];
|
|
|
|
|
const cv::Rect &roi = cand.protr.roi;
|
|
|
|
|
if (roi.width <= 0 || roi.height <= 0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
@ -3304,12 +3097,12 @@ int ImgCheckAnalysisy::MatchHandleRegion(const std::vector<Mask_Protrusion_> &ca
|
|
|
|
|
// 宽高:与把手区域外接矩形的宽高相近(允许横竖互换)
|
|
|
|
|
float fw = (float)roi.width;
|
|
|
|
|
float fh = (float)roi.height;
|
|
|
|
|
bool bSizeOK = (fw >= boxRegion.width * fSizeMin && fw <= boxRegion.width * fSizeMax &&
|
|
|
|
|
fh >= boxRegion.height * fSizeMin && fh <= boxRegion.height * fSizeMax);
|
|
|
|
|
bool bSizeOK = (fw >= boxRegion.width * HANDLE_SIZE_MIN_RATIO && fw <= boxRegion.width * HANDLE_SIZE_MAX_RATIO &&
|
|
|
|
|
fh >= boxRegion.height * HANDLE_SIZE_MIN_RATIO && fh <= boxRegion.height * HANDLE_SIZE_MAX_RATIO);
|
|
|
|
|
if (!bSizeOK)
|
|
|
|
|
{
|
|
|
|
|
bSizeOK = (fw >= boxRegion.height * fSizeMin && fw <= boxRegion.height * fSizeMax &&
|
|
|
|
|
fh >= boxRegion.width * fSizeMin && fh <= boxRegion.width * fSizeMax);
|
|
|
|
|
bSizeOK = (fw >= boxRegion.height * HANDLE_SIZE_MIN_RATIO && fw <= boxRegion.height * HANDLE_SIZE_MAX_RATIO &&
|
|
|
|
|
fh >= boxRegion.width * HANDLE_SIZE_MIN_RATIO && fh <= boxRegion.width * HANDLE_SIZE_MAX_RATIO);
|
|
|
|
|
}
|
|
|
|
|
if (!bSizeOK)
|
|
|
|
|
{
|
|
|
|
|
@ -3326,17 +3119,17 @@ int ImgCheckAnalysisy::MatchHandleRegion(const std::vector<Mask_Protrusion_> &ca
|
|
|
|
|
if (nBestIdx < 0 && nNearIdx >= 0)
|
|
|
|
|
{
|
|
|
|
|
// 没匹配上时打印区域内最近的凸起,便于排查阈值
|
|
|
|
|
const cv::Rect &roi = candList[nNearIdx].roi;
|
|
|
|
|
const cv::Rect &roi = candList[nNearIdx].protr.roi;
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle",
|
|
|
|
|
"region box [%d %d %d %d] no match, nearest protrusion corner %d roi [%d %d %d %d] w/h ratio %0.2f %0.2f",
|
|
|
|
|
"region box [%d %d %d %d] no match, nearest protrusion roi [%d %d %d %d] w/h ratio %0.2f %0.2f",
|
|
|
|
|
boxRegion.x, boxRegion.y, boxRegion.width, boxRegion.height,
|
|
|
|
|
candList[nNearIdx].nCorner, roi.x, roi.y, roi.width, roi.height,
|
|
|
|
|
roi.x, roi.y, roi.width, roi.height,
|
|
|
|
|
(float)roi.width / (float)boxRegion.width, (float)roi.height / (float)boxRegion.height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nBestIdx >= 0)
|
|
|
|
|
{
|
|
|
|
|
roiHandle = candList[nBestIdx].roi;
|
|
|
|
|
roiHandle = candList[nBestIdx].protr.roi;
|
|
|
|
|
}
|
|
|
|
|
return nBestIdx;
|
|
|
|
|
}
|
|
|
|
|
|