|
|
|
@ -210,9 +210,15 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pDetConfig->pBaseCheckFunction->edgeDet.bUseAlignRoi)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int re = Draw_Det_Align(img, pDetConfig);
|
|
|
|
|
|
|
|
return re;
|
|
|
|
|
|
|
|
}
|
|
|
|
if (pDetConfig->pBaseCheckFunction->edgeDet.bDrawRoi)
|
|
|
|
if (pDetConfig->pBaseCheckFunction->edgeDet.bDrawRoi)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return Draw_Det(img, pDetConfig);
|
|
|
|
int re = Draw_Det(img, pDetConfig);
|
|
|
|
|
|
|
|
return re;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if (pDetConfig->strChannel == "TA")
|
|
|
|
// if (pDetConfig->strChannel == "TA")
|
|
|
|
@ -313,59 +319,6 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// printf("DirectSign_Right::111111111111111111 re123 %d\n", re123);
|
|
|
|
// printf("DirectSign_Right::111111111111111111 re123 %d\n", re123);
|
|
|
|
|
|
|
|
|
|
|
|
// 求交点
|
|
|
|
|
|
|
|
std::vector<cv::Point2f> cornerPoints;
|
|
|
|
|
|
|
|
cv::Point2f pt;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 左上角:Up_line[0] 与 Left_line[0]
|
|
|
|
|
|
|
|
if (GetSegmentIntersection(Up_line[0], left_line[0], pt))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pt.x += 10;
|
|
|
|
|
|
|
|
pt.y += 10;
|
|
|
|
|
|
|
|
cornerPoints.push_back(pt);
|
|
|
|
|
|
|
|
Up_line[0].p1 = pt;
|
|
|
|
|
|
|
|
left_line[0].p1 = pt;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 右上角:Up_line.back() 与 Right_line[0]
|
|
|
|
|
|
|
|
if (GetSegmentIntersection(Up_line.back(), right_line[0], pt))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pt.x -= 10;
|
|
|
|
|
|
|
|
pt.y += 10;
|
|
|
|
|
|
|
|
cornerPoints.push_back(pt);
|
|
|
|
|
|
|
|
Up_line.back().p2 = pt;
|
|
|
|
|
|
|
|
right_line[0].p1 = pt;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 右下角:Down_line.back() 与 Right_line.back()
|
|
|
|
|
|
|
|
if (GetSegmentIntersection(down_line.back(), right_line.back(), pt))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pt.x -= 10;
|
|
|
|
|
|
|
|
pt.y -= 10;
|
|
|
|
|
|
|
|
cornerPoints.push_back(pt);
|
|
|
|
|
|
|
|
down_line.back().p2 = pt;
|
|
|
|
|
|
|
|
right_line.back().p2 = pt;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 左下角:Down_line[0] 与 Left_line.back()
|
|
|
|
|
|
|
|
if (GetSegmentIntersection(down_line[0], left_line.back(), pt))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pt.x += 10;
|
|
|
|
|
|
|
|
pt.y -= 10;
|
|
|
|
|
|
|
|
cornerPoints.push_back(pt);
|
|
|
|
|
|
|
|
down_line[0].p1 = pt;
|
|
|
|
|
|
|
|
left_line.back().p2 = pt;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 用RANSAC剔除崩边凹陷的异常点后,拟合二次曲线使ROI贴合产品边缘的自然弧度
|
|
|
|
// 用RANSAC剔除崩边凹陷的异常点后,拟合二次曲线使ROI贴合产品边缘的自然弧度
|
|
|
|
// 水平边缘(上下): 拟合 y = a*x² + b*x + c
|
|
|
|
// 水平边缘(上下): 拟合 y = a*x² + b*x + c
|
|
|
|
// 垂直边缘(左右): 拟合 x = a*y² + b*y + c
|
|
|
|
// 垂直边缘(左右): 拟合 x = a*y² + b*y + c
|
|
|
|
@ -487,8 +440,62 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 曲线拟合后重新计算角点,使角点与拟合后的线段端点对齐
|
|
|
|
|
|
|
|
std::vector<cv::Point2f> cornerPoints;
|
|
|
|
|
|
|
|
cv::Point2f pt;
|
|
|
|
|
|
|
|
cornerPoints.clear();
|
|
|
|
|
|
|
|
// 左上角:Up_line[0] 与 left_line[0]
|
|
|
|
|
|
|
|
if (GetSegmentIntersection(Up_line[0], left_line[0], pt))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pt.x += 10;
|
|
|
|
|
|
|
|
pt.y += 10;
|
|
|
|
|
|
|
|
cornerPoints.push_back(pt);
|
|
|
|
|
|
|
|
Up_line[0].p1 = pt;
|
|
|
|
|
|
|
|
left_line[0].p1 = pt;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 右上角:Up_line.back() 与 right_line[0]
|
|
|
|
|
|
|
|
if (GetSegmentIntersection(Up_line.back(), right_line[0], pt))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pt.x -= 10;
|
|
|
|
|
|
|
|
pt.y += 10;
|
|
|
|
|
|
|
|
cornerPoints.push_back(pt);
|
|
|
|
|
|
|
|
Up_line.back().p2 = pt;
|
|
|
|
|
|
|
|
right_line[0].p1 = pt;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 右下角:down_line.back() 与 right_line.back()
|
|
|
|
|
|
|
|
if (GetSegmentIntersection(down_line.back(), right_line.back(), pt))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pt.x -= 10;
|
|
|
|
|
|
|
|
pt.y -= 10;
|
|
|
|
|
|
|
|
cornerPoints.push_back(pt);
|
|
|
|
|
|
|
|
down_line.back().p2 = pt;
|
|
|
|
|
|
|
|
right_line.back().p2 = pt;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 左下角:down_line[0] 与 left_line.back()
|
|
|
|
|
|
|
|
if (GetSegmentIntersection(down_line[0], left_line.back(), pt))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pt.x += 10;
|
|
|
|
|
|
|
|
pt.y -= 10;
|
|
|
|
|
|
|
|
cornerPoints.push_back(pt);
|
|
|
|
|
|
|
|
down_line[0].p1 = pt;
|
|
|
|
|
|
|
|
left_line.back().p2 = pt;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 生成 检测的 roi。
|
|
|
|
// 生成 检测的 roi。
|
|
|
|
int roi_wh = pDetConfig->pBaseCheckFunction->edgeDet.Det_Range;
|
|
|
|
int roi_wh = pDetConfig->pBaseCheckFunction->edgeDet.Det_Range;
|
|
|
|
|
|
|
|
int range_offset = pDetConfig->pBaseCheckFunction->edgeDet.Det_Range_Offset;
|
|
|
|
|
|
|
|
|
|
|
|
cv::Rect imgBounds(0, 0, img.cols, img.rows); // 图像边界,用于裁剪越界 ROI
|
|
|
|
cv::Rect imgBounds(0, 0, img.cols, img.rows); // 图像边界,用于裁剪越界 ROI
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<Det_ROI_Config> up_det_roi;
|
|
|
|
std::vector<Det_ROI_Config> up_det_roi;
|
|
|
|
@ -498,52 +505,56 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
for (const auto &line : Up_line)
|
|
|
|
for (const auto &line : Up_line)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
tem.plist.push_back(line.p1);
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x, line.p1.y + range_offset));
|
|
|
|
tem.plist.push_back(line.p2);
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x, line.p2.y + range_offset));
|
|
|
|
|
|
|
|
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x, line.p2.y + roi_wh));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x, line.p2.y + range_offset + roi_wh));
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x, line.p1.y + roi_wh));
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x, line.p1.y + range_offset + roi_wh));
|
|
|
|
tem.roi = cv::boundingRect(tem.plist) & imgBounds;
|
|
|
|
tem.rrect = cv::minAreaRect(tem.plist);
|
|
|
|
if (tem.roi.width <= 0 || tem.roi.height <= 0) continue;
|
|
|
|
tem.roi = tem.rrect.boundingRect() & imgBounds;
|
|
|
|
|
|
|
|
if (tem.rrect.size.width <= 0 || tem.rrect.size.height <= 0) continue;
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
up_det_roi.push_back(tem);
|
|
|
|
up_det_roi.push_back(tem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto &line : down_line)
|
|
|
|
for (const auto &line : down_line)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
tem.plist.push_back(line.p1);
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x, line.p1.y - range_offset));
|
|
|
|
tem.plist.push_back(line.p2);
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x, line.p2.y - range_offset));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x, line.p2.y - roi_wh));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x, line.p2.y - range_offset - roi_wh));
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x, line.p1.y - roi_wh));
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x, line.p1.y - range_offset - roi_wh));
|
|
|
|
|
|
|
|
|
|
|
|
tem.roi = cv::boundingRect(tem.plist) & imgBounds;
|
|
|
|
tem.rrect = cv::minAreaRect(tem.plist);
|
|
|
|
if (tem.roi.width <= 0 || tem.roi.height <= 0) continue;
|
|
|
|
tem.roi = tem.rrect.boundingRect() & imgBounds;
|
|
|
|
|
|
|
|
if (tem.rrect.size.width <= 0 || tem.rrect.size.height <= 0) continue;
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
down_det_roi.push_back(tem);
|
|
|
|
down_det_roi.push_back(tem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto &line : left_line)
|
|
|
|
for (const auto &line : left_line)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
tem.plist.push_back(line.p1);
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x + range_offset, line.p1.y));
|
|
|
|
tem.plist.push_back(line.p2);
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x + range_offset, line.p2.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x + roi_wh, line.p2.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x + range_offset + roi_wh, line.p2.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x + roi_wh, line.p1.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x + range_offset + roi_wh, line.p1.y));
|
|
|
|
|
|
|
|
|
|
|
|
tem.roi = cv::boundingRect(tem.plist) & imgBounds;
|
|
|
|
tem.rrect = cv::minAreaRect(tem.plist);
|
|
|
|
if (tem.roi.width <= 0 || tem.roi.height <= 0) continue;
|
|
|
|
tem.roi = tem.rrect.boundingRect() & imgBounds;
|
|
|
|
|
|
|
|
if (tem.rrect.size.width <= 0 || tem.rrect.size.height <= 0) continue;
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
left_det_roi.push_back(tem);
|
|
|
|
left_det_roi.push_back(tem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto &line : right_line)
|
|
|
|
for (const auto &line : right_line)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
tem.plist.push_back(line.p1);
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x - range_offset, line.p1.y));
|
|
|
|
tem.plist.push_back(line.p2);
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x - range_offset, line.p2.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x - roi_wh, line.p2.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x - range_offset - roi_wh, line.p2.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x - roi_wh, line.p1.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p1.x - range_offset - roi_wh, line.p1.y));
|
|
|
|
|
|
|
|
|
|
|
|
tem.roi = cv::boundingRect(tem.plist) & imgBounds;
|
|
|
|
tem.rrect = cv::minAreaRect(tem.plist);
|
|
|
|
if (tem.roi.width <= 0 || tem.roi.height <= 0) continue;
|
|
|
|
tem.roi = tem.rrect.boundingRect() & imgBounds;
|
|
|
|
|
|
|
|
if (tem.rrect.size.width <= 0 || tem.rrect.size.height <= 0) continue;
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
right_det_roi.push_back(tem);
|
|
|
|
right_det_roi.push_back(tem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -602,7 +613,10 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto &r : pDetConfig->edge_det_roi)
|
|
|
|
for (const auto &r : pDetConfig->edge_det_roi)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cv::rectangle(showimg, r.roi, cv::Scalar(255, 0, 255), 3); // 黄色角点
|
|
|
|
cv::Point2f vertices[4];
|
|
|
|
|
|
|
|
r.rrect.points(vertices);
|
|
|
|
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
|
|
|
|
|
|
cv::line(showimg, vertices[j], vertices[(j+1)%4], cv::Scalar(255, 0, 255), 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto r : pDetConfig->qx_result)
|
|
|
|
for (const auto r : pDetConfig->qx_result)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -1072,29 +1086,38 @@ int Edge_QX_Det::Det_qx(const cv::Mat &img, std::vector<Det_ROI_Config> roilist,
|
|
|
|
return -12;
|
|
|
|
return -12;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Base_Function_Edge_Det *pedgeDet = &pDetConfig->pBaseCheckFunction->edgeDet;
|
|
|
|
Base_Function_Edge_Det *pedgeDet = &pDetConfig->pBaseCheckFunction->edgeDet;
|
|
|
|
vector<cv::Point> allPoints;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (roilist.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::Rect DetRoi;
|
|
|
|
for (const auto &ROI : roilist)
|
|
|
|
for (const auto &ROI : roilist)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
allPoints.insert(allPoints.end(), ROI.plist.begin(), ROI.plist.end());
|
|
|
|
DetRoi = DetRoi | ROI.roi;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (allPoints.size() <= 0)
|
|
|
|
if (DetRoi.width <= 0 || DetRoi.height <= 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cv::Rect DetRoi = cv::boundingRect(allPoints);
|
|
|
|
|
|
|
|
cv::Mat roiMask = cv::Mat::zeros(DetRoi.height, DetRoi.width, CV_8UC1);
|
|
|
|
cv::Mat roiMask = cv::Mat::zeros(DetRoi.height, DetRoi.width, CV_8UC1);
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto &ROI : roilist)
|
|
|
|
for (const auto &ROI : roilist)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cv::Mat mask;
|
|
|
|
cv::Mat mask;
|
|
|
|
applyMaskInROI(img, ROI, mask, pedgeDet->Det_threshold);
|
|
|
|
applyMaskInROI(img, ROI, mask, pedgeDet->Det_threshold);
|
|
|
|
|
|
|
|
if (mask.empty())
|
|
|
|
|
|
|
|
continue;
|
|
|
|
cv::Rect detimg_roi;
|
|
|
|
cv::Rect detimg_roi;
|
|
|
|
detimg_roi.x = ROI.roi.x - DetRoi.x;
|
|
|
|
detimg_roi.x = ROI.roi.x - DetRoi.x;
|
|
|
|
detimg_roi.y = ROI.roi.y - DetRoi.y;
|
|
|
|
detimg_roi.y = ROI.roi.y - DetRoi.y;
|
|
|
|
detimg_roi.width = ROI.roi.width;
|
|
|
|
detimg_roi.width = mask.cols; // 使用 mask 实际尺寸,而非 ROI.roi 尺寸,避免裁剪后不一致
|
|
|
|
detimg_roi.height = ROI.roi.height;
|
|
|
|
detimg_roi.height = mask.rows;
|
|
|
|
mask.copyTo(roiMask(detimg_roi), mask);
|
|
|
|
// 安全检查:确保 detimg_roi 在 roiMask 范围内
|
|
|
|
|
|
|
|
cv::Rect safeDetRoi = detimg_roi & cv::Rect(0, 0, roiMask.cols, roiMask.rows);
|
|
|
|
|
|
|
|
if (safeDetRoi.width <= 0 || safeDetRoi.height <= 0)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mask.copyTo(roiMask(safeDetRoi), mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int erx = pedgeDet->QX_Widht_min;
|
|
|
|
int erx = pedgeDet->QX_Widht_min;
|
|
|
|
int ery = pedgeDet->QX_Height_min;
|
|
|
|
int ery = pedgeDet->QX_Height_min;
|
|
|
|
@ -1257,6 +1280,66 @@ int Edge_QX_Det::Det_qx(const cv::Mat &img, std::vector<Det_ROI_Config> roilist,
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 多边形模式缺陷检测:无方向/角点过滤,逐ROI独立处理避免巨型mask =====
|
|
|
|
|
|
|
|
int Edge_QX_Det::Det_qx_polygon(const cv::Mat &img, const std::vector<Det_ROI_Config> &roilist, DetConfigResult *pDetConfig)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (img.empty()) return -11;
|
|
|
|
|
|
|
|
if (img.channels() != 1) return -12;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Base_Function_Edge_Det *pedgeDet = &pDetConfig->pBaseCheckFunction->edgeDet;
|
|
|
|
|
|
|
|
if (roilist.empty()) return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int erx = std::max(pedgeDet->QX_Widht_min, 3);
|
|
|
|
|
|
|
|
int ery = std::max(pedgeDet->QX_Height_min, 3);
|
|
|
|
|
|
|
|
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(erx, ery));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto &ROI : roilist)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Rect safeRoi = ROI.roi & cv::Rect(0, 0, img.cols, img.rows);
|
|
|
|
|
|
|
|
if (safeRoi.width <= 0 || safeRoi.height <= 0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 二值化:暗区(产品)=255
|
|
|
|
|
|
|
|
cv::Mat binary;
|
|
|
|
|
|
|
|
cv::compare(img(safeRoi), pedgeDet->Det_threshold, binary, cv::CMP_LT);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 多边形 mask(局部坐标)
|
|
|
|
|
|
|
|
std::vector<cv::Point> localPts;
|
|
|
|
|
|
|
|
localPts.reserve(ROI.plist.size());
|
|
|
|
|
|
|
|
for (const auto &pt : ROI.plist)
|
|
|
|
|
|
|
|
localPts.emplace_back(pt.x - safeRoi.x, pt.y - safeRoi.y);
|
|
|
|
|
|
|
|
cv::Mat polyMask = cv::Mat::zeros(safeRoi.size(), CV_8UC1);
|
|
|
|
|
|
|
|
cv::fillPoly(polyMask, std::vector<std::vector<cv::Point>>{localPts}, 255);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 应用 mask
|
|
|
|
|
|
|
|
cv::Mat result = cv::Mat::zeros(safeRoi.size(), CV_8UC1);
|
|
|
|
|
|
|
|
binary.copyTo(result, polyMask);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 形态学开操作
|
|
|
|
|
|
|
|
cv::morphologyEx(result, result, cv::MORPH_OPEN, kernel);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 找轮廓 → 尺寸过滤
|
|
|
|
|
|
|
|
std::vector<std::vector<cv::Point>> contours;
|
|
|
|
|
|
|
|
cv::findContours(result, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < contours.size(); ++i)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Rect rect = cv::boundingRect(contours[i]);
|
|
|
|
|
|
|
|
if (rect.width >= pedgeDet->QX_Widht_min && rect.width <= pedgeDet->QX_Widht_max &&
|
|
|
|
|
|
|
|
rect.height >= pedgeDet->QX_Height_min && rect.height <= pedgeDet->QX_Height_max)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
QX_Result tem;
|
|
|
|
|
|
|
|
tem.roi_src.x = rect.x + safeRoi.x;
|
|
|
|
|
|
|
|
tem.roi_src.y = rect.y + safeRoi.y;
|
|
|
|
|
|
|
|
tem.roi_src.width = rect.width;
|
|
|
|
|
|
|
|
tem.roi_src.height = rect.height;
|
|
|
|
|
|
|
|
tem.area_pixel = cv::contourArea(contours[i]);
|
|
|
|
|
|
|
|
pDetConfig->qx_result.push_back(tem);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Edge_QX_Det::applyMaskInROI(const cv::Mat &grayImg, const Det_ROI_Config &config, cv::Mat &result, int threshold)
|
|
|
|
int Edge_QX_Det::applyMaskInROI(const cv::Mat &grayImg, const Det_ROI_Config &config, cv::Mat &result, int threshold)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 0. 裁剪 ROI,防止 RANSAC 投影后越界
|
|
|
|
// 0. 裁剪 ROI,防止 RANSAC 投影后越界
|
|
|
|
@ -1268,11 +1351,9 @@ int Edge_QX_Det::applyMaskInROI(const cv::Mat &grayImg, const Det_ROI_Config &co
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 1. 获取 ROI 区域图像(不 clone,只引用)
|
|
|
|
// 1. 获取 ROI 区域图像(不 clone,只引用)
|
|
|
|
cv::Mat roiGray = grayImg(safeRoi);
|
|
|
|
cv::Mat roiGray = grayImg(safeRoi);
|
|
|
|
// cv::imwrite("roiGray.png", roiGray);
|
|
|
|
|
|
|
|
// 2. 二值化(用 compare 更快)
|
|
|
|
// 2. 二值化(用 compare 更快)
|
|
|
|
cv::Mat binary;
|
|
|
|
cv::Mat binary;
|
|
|
|
cv::compare(roiGray, threshold, binary, cv::CMP_LT); // binary = roiGray > 128 ? 255 : 0
|
|
|
|
cv::compare(roiGray, threshold, binary, cv::CMP_LT); // binary = gray < threshold ? 255 : 0
|
|
|
|
// cv::imwrite("binary.png", binary);
|
|
|
|
|
|
|
|
// 3. 构建局部坐标的多边形(相对于裁剪后的 safeRoi)
|
|
|
|
// 3. 构建局部坐标的多边形(相对于裁剪后的 safeRoi)
|
|
|
|
std::vector<cv::Point> localPts;
|
|
|
|
std::vector<cv::Point> localPts;
|
|
|
|
localPts.reserve(config.plist.size());
|
|
|
|
localPts.reserve(config.plist.size());
|
|
|
|
@ -1282,11 +1363,9 @@ int Edge_QX_Det::applyMaskInROI(const cv::Mat &grayImg, const Det_ROI_Config &co
|
|
|
|
// 4. 快速创建 mask 并填充
|
|
|
|
// 4. 快速创建 mask 并填充
|
|
|
|
cv::Mat mask = cv::Mat::zeros(roiGray.size(), CV_8UC1);
|
|
|
|
cv::Mat mask = cv::Mat::zeros(roiGray.size(), CV_8UC1);
|
|
|
|
cv::fillPoly(mask, std::vector<std::vector<cv::Point>>{localPts}, 255);
|
|
|
|
cv::fillPoly(mask, std::vector<std::vector<cv::Point>>{localPts}, 255);
|
|
|
|
// cv::imwrite("mask.png", mask);
|
|
|
|
|
|
|
|
// 5. 应用 mask
|
|
|
|
// 5. 应用 mask
|
|
|
|
result = cv::Mat::zeros(roiGray.size(), CV_8UC1);
|
|
|
|
result = cv::Mat::zeros(roiGray.size(), CV_8UC1);
|
|
|
|
binary.copyTo(result, mask);
|
|
|
|
binary.copyTo(result, mask);
|
|
|
|
// cv::imwrite("result.png", result);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -1313,8 +1392,8 @@ int Edge_QX_Det::Draw_Det(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
cv::Point pc;
|
|
|
|
cv::Point pc;
|
|
|
|
if (pDetConfig->alginResult.H.empty())
|
|
|
|
if (pDetConfig->alginResult.H.empty())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
pc.x = p.x - pDetConfig->alginResult.corpRoi.x + pDetConfig->alginResult.offtx;
|
|
|
|
pc.x = p.x - pDetConfig->alginResult.corpRoi.x;
|
|
|
|
pc.y = p.y - pDetConfig->alginResult.corpRoi.y + pDetConfig->alginResult.offty;
|
|
|
|
pc.y = p.y - pDetConfig->alginResult.corpRoi.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -1397,6 +1476,190 @@ int Edge_QX_Det::Draw_Det(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int Edge_QX_Det::Draw_Det_Align(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Base_Function_Edge_Det *pedgeDet = &pDetConfig->pBaseCheckFunction->edgeDet;
|
|
|
|
|
|
|
|
if (pedgeDet->Align_region.size() <= 2)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 第1步:精细化边缘点 =====
|
|
|
|
|
|
|
|
// 在每个Align_region点周围100x100区域内,用二值化+轮廓找最接近中心的实际边缘点
|
|
|
|
|
|
|
|
std::vector<cv::Point> Det_region;
|
|
|
|
|
|
|
|
for(auto p : pedgeDet->Align_region)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Rect orig_roi(p.x - 50, p.y - 50, 100, 100);
|
|
|
|
|
|
|
|
cv::Rect c_roi_100 = orig_roi & cv::Rect(0, 0, img.cols, img.rows);
|
|
|
|
|
|
|
|
if (c_roi_100.width <= 0 || c_roi_100.height <= 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Det_region.push_back(p);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::Mat c_img = img(c_roi_100);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 二值化:产品区域为暗(低灰度),背景为亮(高灰度),THRESH_BINARY_INV 使暗区变白
|
|
|
|
|
|
|
|
cv::Mat binary;
|
|
|
|
|
|
|
|
cv::threshold(c_img, binary, pedgeDet->Det_threshold, 255, cv::THRESH_BINARY_INV);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 找轮廓
|
|
|
|
|
|
|
|
std::vector<std::vector<cv::Point>> contours;
|
|
|
|
|
|
|
|
cv::findContours(binary, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 找最接近中心的边缘点
|
|
|
|
|
|
|
|
// 中心在原始100x100区域中为(50,50),裁剪后需要偏移
|
|
|
|
|
|
|
|
cv::Point center_in_roi(50 - (orig_roi.x - c_roi_100.x), 50 - (orig_roi.y - c_roi_100.y));
|
|
|
|
|
|
|
|
cv::Point best_pt;
|
|
|
|
|
|
|
|
double best_dist = 1e10;
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& contour : contours)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (const auto& pt : contour)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
double dist = (pt.x - center_in_roi.x) * (pt.x - center_in_roi.x) +
|
|
|
|
|
|
|
|
(pt.y - center_in_roi.y) * (pt.y - center_in_roi.y);
|
|
|
|
|
|
|
|
if (dist < best_dist)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
best_dist = dist;
|
|
|
|
|
|
|
|
best_pt = pt;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
|
|
|
|
Det_region.push_back(cv::Point(best_pt.x + c_roi_100.x, best_pt.y + c_roi_100.y));
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
Det_region.push_back(p); // 未找到边缘点则回退使用原始点
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (Det_region.size() <= 2)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 第2步:计算向内偏移的多边形 =====
|
|
|
|
|
|
|
|
// 使用多边形有向面积判断顶点顺序,确保向内法向量方向正确
|
|
|
|
|
|
|
|
int inward_offset = pedgeDet->Det_Range; // 使用配置的检测范围作为内缩距离
|
|
|
|
|
|
|
|
int range_offset = pedgeDet->Det_Range_Offset; // 可选的额外偏移量
|
|
|
|
|
|
|
|
if (inward_offset <= 0) inward_offset = 50;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t n = Det_region.size();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算有向面积判断多边形方向(CCW为正)
|
|
|
|
|
|
|
|
float signed_area = 0;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
size_t j = (i + 1) % n;
|
|
|
|
|
|
|
|
signed_area += (float)Det_region[i].x * Det_region[j].y -
|
|
|
|
|
|
|
|
(float)Det_region[j].x * Det_region[i].y;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isCCW = (signed_area > 0); // 逆时针: 左法线指向内部
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<cv::Point2f> inner_pts(n);
|
|
|
|
|
|
|
|
std::vector<cv::Point2f> outer_pts(n);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Point2f prev = Det_region[(i + n - 1) % n];
|
|
|
|
|
|
|
|
cv::Point2f curr = Det_region[i];
|
|
|
|
|
|
|
|
cv::Point2f next = Det_region[(i + 1) % n];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 入边方向
|
|
|
|
|
|
|
|
cv::Point2f dir1 = curr - prev;
|
|
|
|
|
|
|
|
float len1 = cv::norm(dir1);
|
|
|
|
|
|
|
|
if (len1 > 1e-6f) dir1 = dir1 / len1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 出边方向
|
|
|
|
|
|
|
|
cv::Point2f dir2 = next - curr;
|
|
|
|
|
|
|
|
float len2 = cv::norm(dir2);
|
|
|
|
|
|
|
|
if (len2 > 1e-6f) dir2 = dir2 / len2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 向内法向量
|
|
|
|
|
|
|
|
cv::Point2f normal1, normal2;
|
|
|
|
|
|
|
|
if (isCCW)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
normal1 = cv::Point2f(-dir1.y, dir1.x); // 左法线 = 向内(CCW)
|
|
|
|
|
|
|
|
normal2 = cv::Point2f(-dir2.y, dir2.x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
normal1 = cv::Point2f(dir1.y, -dir1.x); // 右法线 = 向内(CW)
|
|
|
|
|
|
|
|
normal2 = cv::Point2f(dir2.y, -dir2.x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 角平分线方向
|
|
|
|
|
|
|
|
cv::Point2f bisector = normal1 + normal2;
|
|
|
|
|
|
|
|
float bis_len = cv::norm(bisector);
|
|
|
|
|
|
|
|
if (bis_len < 1e-6f)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
bisector = normal1; // 两边共线退化情况
|
|
|
|
|
|
|
|
bis_len = cv::norm(bisector);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bis_len > 1e-6f)
|
|
|
|
|
|
|
|
bisector = bisector / bis_len;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 内边界 = range_offset + inward_offset(ROI高度仍为 inward_offset)
|
|
|
|
|
|
|
|
inner_pts[i] = curr + (range_offset + inward_offset) * bisector;
|
|
|
|
|
|
|
|
// 外边界 = range_offset(整个ROI向产品内部平移 range_offset)
|
|
|
|
|
|
|
|
outer_pts[i] = curr + range_offset * bisector;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 第3步:生成检测ROI =====
|
|
|
|
|
|
|
|
cv::Rect imgBounds(0, 0, img.cols, img.rows);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
size_t j = (i + 1) % n;
|
|
|
|
|
|
|
|
Det_ROI_Config tem;
|
|
|
|
|
|
|
|
tem.plist.push_back(cv::Point(cvRound(outer_pts[i].x), cvRound(outer_pts[i].y)));
|
|
|
|
|
|
|
|
tem.plist.push_back(cv::Point(cvRound(outer_pts[j].x), cvRound(outer_pts[j].y)));
|
|
|
|
|
|
|
|
tem.plist.push_back(cv::Point(cvRound(inner_pts[j].x), cvRound(inner_pts[j].y)));
|
|
|
|
|
|
|
|
tem.plist.push_back(cv::Point(cvRound(inner_pts[i].x), cvRound(inner_pts[i].y)));
|
|
|
|
|
|
|
|
tem.rrect = cv::minAreaRect(tem.plist);
|
|
|
|
|
|
|
|
tem.roi = tem.rrect.boundingRect() & imgBounds;
|
|
|
|
|
|
|
|
if (tem.rrect.size.width <= 0 || tem.rrect.size.height <= 0) continue;
|
|
|
|
|
|
|
|
pDetConfig->edge_det_roi.push_back(tem);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// ===== 第4步:缺陷检测(多边形模式,逐ROI独立处理) =====
|
|
|
|
|
|
|
|
Det_qx_polygon(img, pDetConfig->edge_det_roi, pDetConfig);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bshowimg)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::cvtColor(img, showimg, cv::COLOR_GRAY2BGR);
|
|
|
|
|
|
|
|
// 绘制外多边形(边缘点连线)
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
size_t j = (i + 1) % n;
|
|
|
|
|
|
|
|
cv::line(showimg, Det_region[i], Det_region[j], cv::Scalar(0, 255, 0), 2);
|
|
|
|
|
|
|
|
cv::circle(showimg, Det_region[i], 4, cv::Scalar(0, 255, 255), -1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 绘制内缩多边形
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
size_t j = (i + 1) % n;
|
|
|
|
|
|
|
|
cv::Point pi(cvRound(inner_pts[i].x), cvRound(inner_pts[i].y));
|
|
|
|
|
|
|
|
cv::Point pj(cvRound(inner_pts[j].x), cvRound(inner_pts[j].y));
|
|
|
|
|
|
|
|
cv::line(showimg, pi, pj, cv::Scalar(255, 0, 0), 2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 绘制检测ROI
|
|
|
|
|
|
|
|
for (const auto &r : pDetConfig->edge_det_roi)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Point2f vertices[4];
|
|
|
|
|
|
|
|
r.rrect.points(vertices);
|
|
|
|
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
|
|
|
|
|
|
cv::line(showimg, vertices[j], vertices[(j+1)%4], cv::Scalar(255, 0, 255), 2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 绘制检测到的缺陷
|
|
|
|
|
|
|
|
for (const auto &r : pDetConfig->qx_result)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::rectangle(showimg, r.roi_src, cv::Scalar(255, 22, 100), 3);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::imwrite(pDetConfig->strChannel + "_edge_align_show.png", showimg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Edge_QX_Det::UDNoiseEdgeDetect(cv::Mat img, int DirectSign, int Gate, int BorW, cv::Rect roi, int StepCount, int Limit)
|
|
|
|
int Edge_QX_Det::UDNoiseEdgeDetect(cv::Mat img, int DirectSign, int Gate, int BorW, cv::Rect roi, int StepCount, int Limit)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (img.empty())
|
|
|
|
if (img.empty())
|
|
|
|
|