|
|
|
@ -31,7 +31,9 @@ float pointToLineDistance(const cv::Point &pt, const cv::Vec4f &line)
|
|
|
|
float x = pt.x, y = pt.y;
|
|
|
|
float x = pt.x, y = pt.y;
|
|
|
|
return std::abs(vy * x - vx * y + (vx * y0 - vy * x0)) / std::sqrt(vx * vx + vy * vy);
|
|
|
|
return std::abs(vy * x - vx * y + (vx * y0 - vy * x0)) / std::sqrt(vx * vx + vy * vy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool FitLineWithOutlierRemoval(const std::vector<cv::Point> &inputPoints, cv::Vec4f &outputLine, int ransacIters = 100, float inlierThresh = 10)
|
|
|
|
bool FitLineWithOutlierRemoval(const std::vector<cv::Point> &inputPoints, cv::Vec4f &outputLine,
|
|
|
|
|
|
|
|
int ransacIters = 100, float inlierThresh = 10,
|
|
|
|
|
|
|
|
std::vector<cv::Point> *outInliers = nullptr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (inputPoints.size() < 2)
|
|
|
|
if (inputPoints.size() < 2)
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
@ -80,10 +82,62 @@ bool FitLineWithOutlierRemoval(const std::vector<cv::Point> &inputPoints, cv::Ve
|
|
|
|
if (bestInlierPoints.size() < 2)
|
|
|
|
if (bestInlierPoints.size() < 2)
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 输出 inlier 点集,供后续曲线拟合使用
|
|
|
|
|
|
|
|
if (outInliers)
|
|
|
|
|
|
|
|
*outInliers = bestInlierPoints;
|
|
|
|
|
|
|
|
|
|
|
|
cv::fitLine(bestInlierPoints, outputLine, cv::DIST_L2, 0, 0.01, 0.01);
|
|
|
|
cv::fitLine(bestInlierPoints, outputLine, cv::DIST_L2, 0, 0.01, 0.01);
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 二次曲线拟合(最小二乘法)
|
|
|
|
|
|
|
|
// isHorizontal=true: 拟合 y = a*x² + b*x + c,coeffs = [a, b, c]
|
|
|
|
|
|
|
|
// isHorizontal=false: 拟合 x = a*y² + b*y + c,coeffs = [a, b, c]
|
|
|
|
|
|
|
|
bool FitQuadraticCurve(const std::vector<cv::Point> &points, cv::Vec3f &coeffs, bool isHorizontal)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (points.size() < 3)
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int n = (int)points.size();
|
|
|
|
|
|
|
|
cv::Mat A(n, 3, CV_64F);
|
|
|
|
|
|
|
|
cv::Mat B(n, 1, CV_64F);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < n; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
double t = isHorizontal ? points[i].x : points[i].y;
|
|
|
|
|
|
|
|
double v = isHorizontal ? points[i].y : points[i].x;
|
|
|
|
|
|
|
|
A.at<double>(i, 0) = t * t;
|
|
|
|
|
|
|
|
A.at<double>(i, 1) = t;
|
|
|
|
|
|
|
|
A.at<double>(i, 2) = 1.0;
|
|
|
|
|
|
|
|
B.at<double>(i, 0) = v;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat X;
|
|
|
|
|
|
|
|
if (!cv::solve(A, B, X, cv::DECOMP_SVD))
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
coeffs[0] = (float)X.at<double>(0, 0);
|
|
|
|
|
|
|
|
coeffs[1] = (float)X.at<double>(1, 0);
|
|
|
|
|
|
|
|
coeffs[2] = (float)X.at<double>(2, 0);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将点投影到二次曲线上(垂直投影:保持 x 不变求 y,或保持 y 不变求 x)
|
|
|
|
|
|
|
|
cv::Point ProjectPointToCurve(const cv::Point &pt, const cv::Vec3f &coeffs, bool isHorizontal)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
float a = coeffs[0], b = coeffs[1], c = coeffs[2];
|
|
|
|
|
|
|
|
if (isHorizontal)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
float y = a * pt.x * pt.x + b * pt.x + c;
|
|
|
|
|
|
|
|
return cv::Point(pt.x, cvRound(y));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
float x = a * pt.y * pt.y + b * pt.y + c;
|
|
|
|
|
|
|
|
return cv::Point(cvRound(x), pt.y);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void drawFittedLine(cv::Mat &image, const cv::Vec4f &line, const cv::Scalar &color, int thickness = 2)
|
|
|
|
void drawFittedLine(cv::Mat &image, const cv::Vec4f &line, const cv::Scalar &color, int thickness = 2)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
double scale = std::max(image.cols, image.rows) * 2.0;
|
|
|
|
double scale = std::max(image.cols, image.rows) * 2.0;
|
|
|
|
@ -312,8 +366,130 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
else
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 用RANSAC剔除崩边凹陷的异常点后,拟合二次曲线使ROI贴合产品边缘的自然弧度
|
|
|
|
|
|
|
|
// 水平边缘(上下): 拟合 y = a*x² + b*x + c
|
|
|
|
|
|
|
|
// 垂直边缘(左右): 拟合 x = a*y² + b*y + c
|
|
|
|
|
|
|
|
// 若曲线拟合失败(点数不足), 则回退到直线投影
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 上边缘:RANSAC获取inlier → 二次曲线拟合 → 投影到曲线
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Vec4f fittedLine_up;
|
|
|
|
|
|
|
|
std::vector<cv::Point> inliers_up;
|
|
|
|
|
|
|
|
cv::Vec3f curveCoeffs_up;
|
|
|
|
|
|
|
|
bool useCurve = false;
|
|
|
|
|
|
|
|
if (FitLineWithOutlierRemoval(up_edge, fittedLine_up, 100, 15, &inliers_up))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
useCurve = FitQuadraticCurve(inliers_up, curveCoeffs_up, true);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& line : Up_line)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (useCurve)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
line.p1 = ProjectPointToCurve(line.p1, curveCoeffs_up, true);
|
|
|
|
|
|
|
|
line.p2 = ProjectPointToCurve(line.p2, curveCoeffs_up, true);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
float vx = fittedLine_up[0], vy = fittedLine_up[1], x0 = fittedLine_up[2], y0 = fittedLine_up[3];
|
|
|
|
|
|
|
|
float denom = vx * vx + vy * vy;
|
|
|
|
|
|
|
|
float t1 = ((line.p1.x - x0) * vx + (line.p1.y - y0) * vy) / denom;
|
|
|
|
|
|
|
|
float t2 = ((line.p2.x - x0) * vx + (line.p2.y - y0) * vy) / denom;
|
|
|
|
|
|
|
|
line.p1 = cv::Point(cvRound(x0 + t1 * vx), cvRound(y0 + t1 * vy));
|
|
|
|
|
|
|
|
line.p2 = cv::Point(cvRound(x0 + t2 * vx), cvRound(y0 + t2 * vy));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 下边缘
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Vec4f fittedLine_down;
|
|
|
|
|
|
|
|
std::vector<cv::Point> inliers_down;
|
|
|
|
|
|
|
|
cv::Vec3f curveCoeffs_down;
|
|
|
|
|
|
|
|
bool useCurve = false;
|
|
|
|
|
|
|
|
if (FitLineWithOutlierRemoval(down_edge, fittedLine_down, 100, 15, &inliers_down))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
useCurve = FitQuadraticCurve(inliers_down, curveCoeffs_down, true);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& line : down_line)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (useCurve)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
line.p1 = ProjectPointToCurve(line.p1, curveCoeffs_down, true);
|
|
|
|
|
|
|
|
line.p2 = ProjectPointToCurve(line.p2, curveCoeffs_down, true);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
float vx = fittedLine_down[0], vy = fittedLine_down[1], x0 = fittedLine_down[2], y0 = fittedLine_down[3];
|
|
|
|
|
|
|
|
float denom = vx * vx + vy * vy;
|
|
|
|
|
|
|
|
float t1 = ((line.p1.x - x0) * vx + (line.p1.y - y0) * vy) / denom;
|
|
|
|
|
|
|
|
float t2 = ((line.p2.x - x0) * vx + (line.p2.y - y0) * vy) / denom;
|
|
|
|
|
|
|
|
line.p1 = cv::Point(cvRound(x0 + t1 * vx), cvRound(y0 + t1 * vy));
|
|
|
|
|
|
|
|
line.p2 = cv::Point(cvRound(x0 + t2 * vx), cvRound(y0 + t2 * vy));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 左边缘
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Vec4f fittedLine_left;
|
|
|
|
|
|
|
|
std::vector<cv::Point> inliers_left;
|
|
|
|
|
|
|
|
cv::Vec3f curveCoeffs_left;
|
|
|
|
|
|
|
|
bool useCurve = false;
|
|
|
|
|
|
|
|
if (FitLineWithOutlierRemoval(left_edge, fittedLine_left, 100, 15, &inliers_left))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
useCurve = FitQuadraticCurve(inliers_left, curveCoeffs_left, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& line : left_line)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (useCurve)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
line.p1 = ProjectPointToCurve(line.p1, curveCoeffs_left, false);
|
|
|
|
|
|
|
|
line.p2 = ProjectPointToCurve(line.p2, curveCoeffs_left, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
float vx = fittedLine_left[0], vy = fittedLine_left[1], x0 = fittedLine_left[2], y0 = fittedLine_left[3];
|
|
|
|
|
|
|
|
float denom = vx * vx + vy * vy;
|
|
|
|
|
|
|
|
float t1 = ((line.p1.x - x0) * vx + (line.p1.y - y0) * vy) / denom;
|
|
|
|
|
|
|
|
float t2 = ((line.p2.x - x0) * vx + (line.p2.y - y0) * vy) / denom;
|
|
|
|
|
|
|
|
line.p1 = cv::Point(cvRound(x0 + t1 * vx), cvRound(y0 + t1 * vy));
|
|
|
|
|
|
|
|
line.p2 = cv::Point(cvRound(x0 + t2 * vx), cvRound(y0 + t2 * vy));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 右边缘
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Vec4f fittedLine_right;
|
|
|
|
|
|
|
|
std::vector<cv::Point> inliers_right;
|
|
|
|
|
|
|
|
cv::Vec3f curveCoeffs_right;
|
|
|
|
|
|
|
|
bool useCurve = false;
|
|
|
|
|
|
|
|
if (FitLineWithOutlierRemoval(right_edge, fittedLine_right, 100, 15, &inliers_right))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
useCurve = FitQuadraticCurve(inliers_right, curveCoeffs_right, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& line : right_line)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (useCurve)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
line.p1 = ProjectPointToCurve(line.p1, curveCoeffs_right, false);
|
|
|
|
|
|
|
|
line.p2 = ProjectPointToCurve(line.p2, curveCoeffs_right, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
float vx = fittedLine_right[0], vy = fittedLine_right[1], x0 = fittedLine_right[2], y0 = fittedLine_right[3];
|
|
|
|
|
|
|
|
float denom = vx * vx + vy * vy;
|
|
|
|
|
|
|
|
float t1 = ((line.p1.x - x0) * vx + (line.p1.y - y0) * vy) / denom;
|
|
|
|
|
|
|
|
float t2 = ((line.p2.x - x0) * vx + (line.p2.y - y0) * vy) / denom;
|
|
|
|
|
|
|
|
line.p1 = cv::Point(cvRound(x0 + t1 * vx), cvRound(y0 + t1 * vy));
|
|
|
|
|
|
|
|
line.p2 = cv::Point(cvRound(x0 + t2 * vx), cvRound(y0 + t2 * vy));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成 检测的 roi。
|
|
|
|
// 生成 检测的 roi。
|
|
|
|
int roi_wh = pDetConfig->pBaseCheckFunction->edgeDet.Det_Range;
|
|
|
|
int roi_wh = pDetConfig->pBaseCheckFunction->edgeDet.Det_Range;
|
|
|
|
|
|
|
|
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;
|
|
|
|
std::vector<Det_ROI_Config> down_det_roi;
|
|
|
|
std::vector<Det_ROI_Config> down_det_roi;
|
|
|
|
@ -327,7 +503,8 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
|
|
|
|
|
|
|
|
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 + 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 + roi_wh));
|
|
|
|
tem.roi = cv::boundingRect(tem.plist);
|
|
|
|
tem.roi = cv::boundingRect(tem.plist) & imgBounds;
|
|
|
|
|
|
|
|
if (tem.roi.width <= 0 || tem.roi.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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -339,7 +516,8 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
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 - 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 - roi_wh));
|
|
|
|
|
|
|
|
|
|
|
|
tem.roi = cv::boundingRect(tem.plist);
|
|
|
|
tem.roi = cv::boundingRect(tem.plist) & imgBounds;
|
|
|
|
|
|
|
|
if (tem.roi.width <= 0 || tem.roi.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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -351,7 +529,8 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x + roi_wh, line.p2.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x + 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 + roi_wh, line.p1.y));
|
|
|
|
|
|
|
|
|
|
|
|
tem.roi = cv::boundingRect(tem.plist);
|
|
|
|
tem.roi = cv::boundingRect(tem.plist) & imgBounds;
|
|
|
|
|
|
|
|
if (tem.roi.width <= 0 || tem.roi.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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -363,7 +542,8 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x - roi_wh, line.p2.y));
|
|
|
|
tem.plist.push_back(cv::Point(line.p2.x - 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 - roi_wh, line.p1.y));
|
|
|
|
|
|
|
|
|
|
|
|
tem.roi = cv::boundingRect(tem.plist);
|
|
|
|
tem.roi = cv::boundingRect(tem.plist) & imgBounds;
|
|
|
|
|
|
|
|
if (tem.roi.width <= 0 || tem.roi.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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -563,11 +743,6 @@ int Edge_QX_Det::GetEdgePoint(const cv::Mat &img, Edge_Search_Config *pEdge_Sear
|
|
|
|
// 对每个搜索点进行 y方向搜索
|
|
|
|
// 对每个搜索点进行 y方向搜索
|
|
|
|
for (int y = sy;; y = y + nCurPoint_Step)
|
|
|
|
for (int y = sy;; y = y + nCurPoint_Step)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (y < 0 || y >= img.rows)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
if (nCurPoint_Step > 0 && y > ey)
|
|
|
|
if (nCurPoint_Step > 0 && y > ey)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -577,7 +752,10 @@ int Edge_QX_Det::GetEdgePoint(const cv::Mat &img, Edge_Search_Config *pEdge_Sear
|
|
|
|
{
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offt = y * img.cols;
|
|
|
|
if (y < 0 || y >= img.rows)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
int range_okNum = 0;
|
|
|
|
int range_okNum = 0;
|
|
|
|
// 对一定范围的点进行判断
|
|
|
|
// 对一定范围的点进行判断
|
|
|
|
for (int k = nrange_start; k < nrange_end; k++)
|
|
|
|
for (int k = nrange_start; k < nrange_end; k++)
|
|
|
|
@ -587,13 +765,13 @@ int Edge_QX_Det::GetEdgePoint(const cv::Mat &img, Edge_Search_Config *pEdge_Sear
|
|
|
|
{
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offt += rangeX;
|
|
|
|
int cur_offt = y * img.cols + rangeX;
|
|
|
|
if (offt < 0 || offt >= img.cols * img.rows)
|
|
|
|
if (cur_offt < 0 || cur_offt >= img.cols * img.rows)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
printf("rangeX %d nCurPoint_Step %d off %d x %d y %d sy %d ey %d %d %d\n", rangeX, nCurPoint_Step, offt, x, y, sy, ey, img.cols, img.rows);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (pdata[offt] >= pEdge_Search_Config->nValueThreshold) // 找到
|
|
|
|
if (pdata[cur_offt] >= pEdge_Search_Config->nValueThreshold) // 找到
|
|
|
|
{
|
|
|
|
{
|
|
|
|
range_okNum++;
|
|
|
|
range_okNum++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -648,13 +826,9 @@ int Edge_QX_Det::GetEdgePoint(const cv::Mat &img, Edge_Search_Config *pEdge_Sear
|
|
|
|
int cur_y = y;
|
|
|
|
int cur_y = y;
|
|
|
|
bool bSucc = false;
|
|
|
|
bool bSucc = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 对每个搜索点进行 y方向搜索
|
|
|
|
// 对每个搜索点进行 x方向搜索
|
|
|
|
for (int x = sx;; x = x + nCurPoint_Step)
|
|
|
|
for (int x = sx;; x = x + nCurPoint_Step)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (x < 0 || x >= img.cols)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
if (nCurPoint_Step > 0 && x > ex)
|
|
|
|
if (nCurPoint_Step > 0 && x > ex)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -664,6 +838,10 @@ int Edge_QX_Det::GetEdgePoint(const cv::Mat &img, Edge_Search_Config *pEdge_Sear
|
|
|
|
{
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x < 0 || x >= img.cols)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int range_okNum = 0;
|
|
|
|
int range_okNum = 0;
|
|
|
|
// 对一定范围的点进行判断
|
|
|
|
// 对一定范围的点进行判断
|
|
|
|
@ -674,12 +852,12 @@ int Edge_QX_Det::GetEdgePoint(const cv::Mat &img, Edge_Search_Config *pEdge_Sear
|
|
|
|
{
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offt = rangey * img.cols + x;
|
|
|
|
int cur_offt = rangey * img.cols + x;
|
|
|
|
if (offt < 0 || offt >= img.cols * img.rows)
|
|
|
|
if (cur_offt < 0 || cur_offt >= img.cols * img.rows)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
printf("rangey %d off %d x %d y %d ey %d %d %d\n", rangey, offt, x, y, ey, img.cols, img.rows);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pdata[offt] >= pEdge_Search_Config->nValueThreshold) // 找到
|
|
|
|
if (pdata[cur_offt] >= pEdge_Search_Config->nValueThreshold) // 找到
|
|
|
|
{
|
|
|
|
{
|
|
|
|
range_okNum++;
|
|
|
|
range_okNum++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -933,12 +1111,14 @@ int Edge_QX_Det::Det_qx(const cv::Mat &img, std::vector<Det_ROI_Config> roilist,
|
|
|
|
|
|
|
|
|
|
|
|
// 进行开操作(先腐蚀后膨胀)可以去除小白点
|
|
|
|
// 进行开操作(先腐蚀后膨胀)可以去除小白点
|
|
|
|
cv::morphologyEx(roiMask, roiMask, cv::MORPH_OPEN, kernel);
|
|
|
|
cv::morphologyEx(roiMask, roiMask, cv::MORPH_OPEN, kernel);
|
|
|
|
|
|
|
|
// getchar();
|
|
|
|
// cv::imwrite("detimg.png", img(DetRoi));
|
|
|
|
// cv::imwrite("detimg.png", img(DetRoi));
|
|
|
|
// cv::imwrite("detimg_mask.png", roiMask);
|
|
|
|
// cv::imwrite("detimg_mask.png", roiMask);
|
|
|
|
// getchar();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool jiao_f_1 = false;
|
|
|
|
bool jiao_f_1 = false;
|
|
|
|
bool jiao_f_2 = false;
|
|
|
|
bool jiao_f_2 = false;
|
|
|
|
|
|
|
|
string jiao_str_1;
|
|
|
|
|
|
|
|
string jiao_str_2;
|
|
|
|
cv::Point jiao_p_1;
|
|
|
|
cv::Point jiao_p_1;
|
|
|
|
cv::Point jiao_p_2;
|
|
|
|
cv::Point jiao_p_2;
|
|
|
|
if (type == Det_ROI_Type_UP)
|
|
|
|
if (type == Det_ROI_Type_UP)
|
|
|
|
@ -947,6 +1127,8 @@ int Edge_QX_Det::Det_qx(const cv::Mat &img, std::vector<Det_ROI_Config> roilist,
|
|
|
|
jiao_f_2 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_RU_Open;
|
|
|
|
jiao_f_2 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_RU_Open;
|
|
|
|
jiao_p_1 = cv::Point(0, 0);
|
|
|
|
jiao_p_1 = cv::Point(0, 0);
|
|
|
|
jiao_p_2 = cv::Point(roiMask.cols, 0);
|
|
|
|
jiao_p_2 = cv::Point(roiMask.cols, 0);
|
|
|
|
|
|
|
|
jiao_str_1 = "queJiao_LU";
|
|
|
|
|
|
|
|
jiao_str_2 = "queJiao_RU";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == Det_ROI_Type_DOWN)
|
|
|
|
else if (type == Det_ROI_Type_DOWN)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -954,49 +1136,105 @@ int Edge_QX_Det::Det_qx(const cv::Mat &img, std::vector<Det_ROI_Config> roilist,
|
|
|
|
jiao_f_2 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_RD_Open;
|
|
|
|
jiao_f_2 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_RD_Open;
|
|
|
|
jiao_p_1 = cv::Point(0, roiMask.rows);
|
|
|
|
jiao_p_1 = cv::Point(0, roiMask.rows);
|
|
|
|
jiao_p_2 = cv::Point(roiMask.cols, roiMask.rows);
|
|
|
|
jiao_p_2 = cv::Point(roiMask.cols, roiMask.rows);
|
|
|
|
|
|
|
|
jiao_str_1 = "queJiao_LD";
|
|
|
|
|
|
|
|
jiao_str_2 = "queJiao_RD";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == Det_ROI_Type_LEFT)
|
|
|
|
else if (type == Det_ROI_Type_LEFT)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
jiao_f_1 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_LU_Open;
|
|
|
|
jiao_f_1 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_LU_Open;
|
|
|
|
jiao_f_2 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_LD_Open;
|
|
|
|
jiao_f_2 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_LD_Open;
|
|
|
|
|
|
|
|
|
|
|
|
jiao_p_1 = cv::Point(0, 0);
|
|
|
|
jiao_p_1 = cv::Point(0, 0);
|
|
|
|
jiao_p_2 = cv::Point(0, roiMask.rows);
|
|
|
|
jiao_p_2 = cv::Point(0, roiMask.rows);
|
|
|
|
|
|
|
|
jiao_str_1 = "queJiao_LU";
|
|
|
|
|
|
|
|
jiao_str_2 = "queJiao_LD";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == Det_ROI_Type_RIGHT)
|
|
|
|
else if (type == Det_ROI_Type_RIGHT)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
jiao_f_1 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_RU_Open;
|
|
|
|
jiao_f_1 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_RU_Open;
|
|
|
|
jiao_f_2 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_RU_Open;
|
|
|
|
jiao_f_2 = pDetConfig->pBaseCheckFunction->edgeDet.queJiao_RD_Open;
|
|
|
|
jiao_p_1 = cv::Point(roiMask.cols, 0);
|
|
|
|
jiao_p_1 = cv::Point(roiMask.cols, 0);
|
|
|
|
jiao_p_2 = cv::Point(roiMask.cols, roiMask.rows);
|
|
|
|
jiao_p_2 = cv::Point(roiMask.cols, roiMask.rows);
|
|
|
|
|
|
|
|
jiao_str_1 = "queJiao_RU";
|
|
|
|
|
|
|
|
jiao_str_2 = "queJiao_RD";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 寻找轮廓
|
|
|
|
// 寻找轮廓
|
|
|
|
vector<vector<Point>> contours;
|
|
|
|
vector<vector<Point>> contours;
|
|
|
|
cv::findContours(roiMask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
|
|
|
|
cv::findContours(roiMask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
|
|
|
|
|
|
// 找到最大面积的轮廓
|
|
|
|
// 缺角过滤配置日志
|
|
|
|
double maxArea = -1;
|
|
|
|
if (pDetConfig->detlog)
|
|
|
|
int maxAreaIdx = -1;
|
|
|
|
{
|
|
|
|
|
|
|
|
pDetConfig->detlog->AddCheckstr(PrintLevel_1, "EdgeQX_Det", "type=%d contours=%zu %s=%d %s=%d thres(w=%d,h=%d)",
|
|
|
|
|
|
|
|
type, contours.size(),
|
|
|
|
|
|
|
|
jiao_str_1.c_str(), jiao_f_1,
|
|
|
|
|
|
|
|
jiao_str_2.c_str(), jiao_f_2,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int jiao_filter_count = 0;
|
|
|
|
for (size_t i = 0; i < contours.size(); ++i)
|
|
|
|
for (size_t i = 0; i < contours.size(); ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cv::Rect rect = cv::boundingRect(contours[i]);
|
|
|
|
cv::Rect rect = cv::boundingRect(contours[i]);
|
|
|
|
cv::Point pc(rect.x + rect.width / 2, rect.y + rect.height / 2);
|
|
|
|
cv::Point pc(rect.x + rect.width / 2, rect.y + rect.height / 2);
|
|
|
|
if (jiao_f_1)
|
|
|
|
if (jiao_f_1)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// printf("1=%d =========== %d %d\n\n", type, abs(pc.x - jiao_p_1.x), abs(pc.y - jiao_p_1.y));
|
|
|
|
int dx1 = abs(pc.x - jiao_p_1.x);
|
|
|
|
if (abs(pc.x - jiao_p_1.x) < pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width && abs(pc.y - jiao_p_1.y) < pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height)
|
|
|
|
int dy1 = abs(pc.y - jiao_p_1.y);
|
|
|
|
|
|
|
|
if (dx1 < pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width && dy1 < pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
if (pDetConfig->detlog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pDetConfig->detlog->AddCheckstr(PrintLevel_2, "EdgeQX_Det", "type=%d %s contour[%zu] center=(%d,%d) coor=(%d,%d) dx=%d dy=%d thres(w=%d,h=%d) --> succ",
|
|
|
|
|
|
|
|
type, jiao_str_1.c_str(), i, pc.x, pc.y, jiao_p_1.x, jiao_p_1.y,
|
|
|
|
|
|
|
|
dx1, dy1,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
jiao_filter_count++;
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (pDetConfig->detlog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pDetConfig->detlog->AddCheckstr(PrintLevel_2, "EdgeQX_Det", "type=%d %s contour[%zu] center=(%d,%d) coor=(%d,%d) dx=%d dy=%d thres(w=%d,h=%d) --> fail",
|
|
|
|
|
|
|
|
type, jiao_str_1.c_str(), i, pc.x, pc.y, jiao_p_1.x, jiao_p_1.y,
|
|
|
|
|
|
|
|
dx1, dy1,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (jiao_f_2)
|
|
|
|
if (jiao_f_2)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// printf("2=%d============ %d %d\n\n", type, abs(pc.x - jiao_p_2.x), abs(pc.y - jiao_p_2.y));
|
|
|
|
int dx2 = abs(pc.x - jiao_p_2.x);
|
|
|
|
if (abs(pc.x - jiao_p_2.x) < pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width && abs(pc.y - jiao_p_2.y) < pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height)
|
|
|
|
int dy2 = abs(pc.y - jiao_p_2.y);
|
|
|
|
|
|
|
|
if (dx2 < pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width && dy2 < pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
if (pDetConfig->detlog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pDetConfig->detlog->AddCheckstr(PrintLevel_2, "EdgeQX_Det", "type=%d %s contour[%zu] center=(%d,%d) coor=(%d,%d) dx=%d dy=%d thres(w=%d,h=%d) --> succ",
|
|
|
|
|
|
|
|
type, jiao_str_2.c_str(), i, pc.x, pc.y, jiao_p_2.x, jiao_p_2.y,
|
|
|
|
|
|
|
|
dx2, dy2,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
jiao_filter_count++;
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (pDetConfig->detlog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pDetConfig->detlog->AddCheckstr(PrintLevel_2, "EdgeQX_Det", "type=%d %s contour[%zu] center=(%d,%d) coor=(%d,%d) dx=%d dy=%d thres(w=%d,h=%d) --> fail",
|
|
|
|
|
|
|
|
type, jiao_str_2.c_str(), i, pc.x, pc.y, jiao_p_2.x, jiao_p_2.y,
|
|
|
|
|
|
|
|
dx2, dy2,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_width,
|
|
|
|
|
|
|
|
pDetConfig->pBaseCheckFunction->edgeDet.queJiao_height);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (rect.width >= pedgeDet->QX_Widht_min && rect.width <= pedgeDet->QX_Widht_max &&
|
|
|
|
if (rect.width >= pedgeDet->QX_Widht_min && rect.width <= pedgeDet->QX_Widht_max &&
|
|
|
|
@ -1021,18 +1259,25 @@ int Edge_QX_Det::Det_qx(const cv::Mat &img, std::vector<Det_ROI_Config> roilist,
|
|
|
|
|
|
|
|
|
|
|
|
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 投影后越界
|
|
|
|
|
|
|
|
cv::Rect safeRoi = config.roi & cv::Rect(0, 0, grayImg.cols, grayImg.rows);
|
|
|
|
|
|
|
|
if (safeRoi.width <= 0 || safeRoi.height <= 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
result = cv::Mat();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
// 1. 获取 ROI 区域图像(不 clone,只引用)
|
|
|
|
// 1. 获取 ROI 区域图像(不 clone,只引用)
|
|
|
|
cv::Mat roiGray = grayImg(config.roi);
|
|
|
|
cv::Mat roiGray = grayImg(safeRoi);
|
|
|
|
// cv::imwrite("roiGray.png", roiGray);
|
|
|
|
// 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 = roiGray > 128 ? 255 : 0
|
|
|
|
// cv::imwrite("binary.png", binary);
|
|
|
|
// cv::imwrite("binary.png", binary);
|
|
|
|
// 3. 构建局部坐标的多边形(避免每次 new)
|
|
|
|
// 3. 构建局部坐标的多边形(相对于裁剪后的 safeRoi)
|
|
|
|
std::vector<cv::Point> localPts;
|
|
|
|
std::vector<cv::Point> localPts;
|
|
|
|
localPts.reserve(config.plist.size());
|
|
|
|
localPts.reserve(config.plist.size());
|
|
|
|
for (const auto &pt : config.plist)
|
|
|
|
for (const auto &pt : config.plist)
|
|
|
|
localPts.emplace_back(pt.x - config.roi.x, pt.y - config.roi.y);
|
|
|
|
localPts.emplace_back(pt.x - safeRoi.x, pt.y - safeRoi.y);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 快速创建 mask 并填充
|
|
|
|
// 4. 快速创建 mask 并填充
|
|
|
|
cv::Mat mask = cv::Mat::zeros(roiGray.size(), CV_8UC1);
|
|
|
|
cv::Mat mask = cv::Mat::zeros(roiGray.size(), CV_8UC1);
|
|
|
|
|