diff --git a/AlgorithmModule/include/ImgCheckAnalysisy.hpp b/AlgorithmModule/include/ImgCheckAnalysisy.hpp index 74e4bc0..834b9e9 100644 --- a/AlgorithmModule/include/ImgCheckAnalysisy.hpp +++ b/AlgorithmModule/include/ImgCheckAnalysisy.hpp @@ -200,7 +200,7 @@ private: int BLobToDetResult(); //自适应参数区域更新 - int Adapt_Config(Mat img, Rect roi, bool b_update, cv::RotatedRect &rot_cur_roi); + int Adapt_Config(Mat img, Rect roi, bool b_update); std::shared_ptr AI_Factory; diff --git a/AlgorithmModule/src/Edge_QX_Det.cpp b/AlgorithmModule/src/Edge_QX_Det.cpp index de06438..73dce5e 100644 --- a/AlgorithmModule/src/Edge_QX_Det.cpp +++ b/AlgorithmModule/src/Edge_QX_Det.cpp @@ -197,6 +197,63 @@ bool Edge_QX_Det::GetSegmentIntersection(const Line &l1, const Line &l2, cv::Poi return false; } +// Liang-Barsky 线段矩形裁剪:把线段截断到矩形范围内,返回是否仍有可见部分。 +// 曲线拟合投影后端点可能越界(如右边缘 x>cols、左边缘 x<0),必须先裁剪, +// 否则角点处的水平/垂直相邻线段可能没有交点。 +static bool ClipLineToRect(cv::Point2f &p1, cv::Point2f &p2, const cv::Rect &r) +{ + float x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y; + float dx = x2 - x1, dy = y2 - y1; + float t0 = 0.f, t1 = 1.f; + + auto clip = [&](float p, float q) -> bool { + if (std::abs(p) < 1e-9f) + { + return q >= 0; // 平行于边界:q<0 表示整段在边界外 + } + float t = q / p; + if (p < 0) + { + if (t > t1) return false; + if (t > t0) t0 = t; + } + else + { + if (t < t0) return false; + if (t < t1) t1 = t; + } + return true; + }; + + if (!clip(-dx, x1 - r.x)) return false; // 左边界 + if (!clip( dx, (r.x + r.width) - x1)) return false; // 右边界 + if (!clip(-dy, y1 - r.y)) return false; // 上边界 + if (!clip( dy, (r.y + r.height) - y1)) return false; // 下边界 + + p1 = cv::Point2f(x1 + t0 * dx, y1 + t0 * dy); + p2 = cv::Point2f(x1 + t1 * dx, y1 + t1 * dy); + return true; +} + +static void ClipLineToImg(cv::Point &p1, cv::Point &p2, const cv::Rect &bounds) +{ + cv::Point2f f1((float)p1.x, (float)p1.y); + cv::Point2f f2((float)p2.x, (float)p2.y); + if (ClipLineToRect(f1, f2, bounds)) + { + p1 = cv::Point(cvRound(f1.x), cvRound(f1.y)); + p2 = cv::Point(cvRound(f2.x), cvRound(f2.y)); + } + else + { + // 完全在图像外:收缩为图像内的最近边界点,避免零长度线段影响角点求交 + cv::Point2f c((float)std::max(0, std::min(bounds.width, (int)((p1.x + p2.x) / 2))), + (float)std::max(0, std::min(bounds.height, (int)((p1.y + p2.y) / 2)))); + p1 = cv::Point(cvRound(c.x), cvRound(c.y)); + p2 = p1; + } +} + Edge_QX_Det::Edge_QX_Det() { bshowimg = false; @@ -444,8 +501,31 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig) std::vector cornerPoints; cv::Point2f pt; cornerPoints.clear(); + + // 曲线投影后端点可能越界(产品部分区域在图像外时),先把所有线段裁剪到图像范围内, + // 否则相邻的水平/垂直线段可能没有交点,导致检测整体失败。 + cv::Rect imgBound(0, 0, img.cols, img.rows); + for (auto &line : Up_line) ClipLineToImg(line.p1, line.p2, imgBound); + for (auto &line : down_line) ClipLineToImg(line.p1, line.p2, imgBound); + for (auto &line : left_line) ClipLineToImg(line.p1, line.p2, imgBound); + for (auto &line : right_line) ClipLineToImg(line.p1, line.p2, imgBound); + + // 求角点:优先线段求交;线段不相交时(越界被裁剪后仍可能无交点)退化为直线求交, + // 并把交点限制在图像范围内。 + auto getCorner = [&](Line &l1, Line &l2, cv::Point2f &out) -> bool { + if (GetSegmentIntersection(l1, l2, out)) + return true; + cv::Vec4f v1((float)(l1.p2.x - l1.p1.x), (float)(l1.p2.y - l1.p1.y), (float)l1.p1.x, (float)l1.p1.y); + cv::Vec4f v2((float)(l2.p2.x - l2.p1.x), (float)(l2.p2.y - l2.p1.y), (float)l2.p1.x, (float)l2.p1.y); + if (!GetLineIntersection(v1, v2, out)) + return false; + out.x = std::max(0.f, std::min((float)img.cols, out.x)); + out.y = std::max(0.f, std::min((float)img.rows, out.y)); + return true; + }; + // 左上角:Up_line[0] 与 left_line[0] - if (GetSegmentIntersection(Up_line[0], left_line[0], pt)) + if (getCorner(Up_line[0], left_line[0], pt)) { pt.x += 10; pt.y += 10; @@ -457,7 +537,7 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig) return 1; // 右上角:Up_line.back() 与 right_line[0] - if (GetSegmentIntersection(Up_line.back(), right_line[0], pt)) + if (getCorner(Up_line.back(), right_line[0], pt)) { pt.x -= 10; pt.y += 10; @@ -469,7 +549,7 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig) return 1; // 右下角:down_line.back() 与 right_line.back() - if (GetSegmentIntersection(down_line.back(), right_line.back(), pt)) + if (getCorner(down_line.back(), right_line.back(), pt)) { pt.x -= 10; pt.y -= 10; @@ -481,7 +561,7 @@ int Edge_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig) return 1; // 左下角:down_line[0] 与 left_line.back() - if (GetSegmentIntersection(down_line[0], left_line.back(), pt)) + if (getCorner(down_line[0], left_line.back(), pt)) { pt.x += 10; pt.y -= 10; diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index d0c8013..bb47f05 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -451,7 +451,7 @@ Point2f GetCoorPoint(Point2f point, Mat img_mat) return new_point; } -int GetEdgeRoi(Mat img, Rect &new_roi, cv::RotatedRect &rotated_roi, float scale_x, float scale_y, string cur_channel){ +int GetEdgeRoi(Mat img, Rect &new_roi, cv::RotatedRect &rotated_roi, float scale_x, float scale_y){ if (img.empty()) { return 1; @@ -465,22 +465,9 @@ int GetEdgeRoi(Mat img, Rect &new_roi, cv::RotatedRect &rotated_roi, float scale // 二值化找最大连通域 Mat r_img_bin; - if(cur_channel == "BCA" || cur_channel == "BTA") - { - threshold(r_img, r_img_bin, 35, 255, THRESH_BINARY); - } - else - { - threshold(r_img, r_img_bin, 35, 255, THRESH_BINARY); - } - // 做一步开运算 - Mat r_img_bin_close; - morphologyEx(r_img_bin, r_img_bin_close, MORPH_CLOSE, Mat::ones(15, 15, CV_8U)); - // imwrite("r_img_bin.tif", r_img_bin); - // imwrite("r_img_bin_close.tif", r_img_bin_close); - // imwrite("r_img.tif", r_img); + threshold(r_img, r_img_bin, 15, 255, THRESH_BINARY); std::vector> contours; - cv::findContours(r_img_bin_close, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + cv::findContours(r_img_bin, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); if (contours.empty()) { return 2; @@ -612,7 +599,7 @@ std::vector sort_vertices(cv::RotatedRect rrect) { return vertices; } -int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, bool b_update, cv::RotatedRect &rot_cur_roi){ +int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, bool b_update){ if (img.empty()) { std::cerr << "Error: Input image 'img' is empty!" << std::endl; return -1; @@ -620,8 +607,8 @@ int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, bool b_update, cv::Ro if(!b_update){ return 1; } - // cv::RotatedRect rot_cur_roi; - int get_edge_roi = GetEdgeRoi(img, cur_roi, rot_cur_roi, 20, 20, m_CheckResult_shareP->basicResult.strChannel); + cv::RotatedRect rot_cur_roi; + int get_edge_roi = GetEdgeRoi(img, cur_roi, rot_cur_roi, 20, 20); if(get_edge_roi != 0){ return 2; } @@ -636,7 +623,6 @@ int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, bool b_update, cv::Ro m_old_cur_markLine_mark1 = m_AnalysisyConfig.baseFunction.markLine.mark_local_1; m_old_cur_markLine_mark2 = m_AnalysisyConfig.baseFunction.markLine.mark_local_2; m_old_cur_regionConfigArr = m_AnalysisyConfig.commonCheckConfig.nodeConfigArr[0].regionConfigArr; - m_old_cur_traditional_region = m_pbaseCheckFunction->traditionDet.detArea; } /*计算old_roi到new_roi的完整仿射变换矩阵(平移+缩放+旋转)*/ @@ -674,8 +660,6 @@ int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, bool b_update, cv::Ro cv::Point& cur_markLine_mark1 = m_AnalysisyConfig.baseFunction.markLine.mark_local_1; cv::Point& cur_markLine_mark2 = m_AnalysisyConfig.baseFunction.markLine.mark_local_2; std::vector& cur_regionConfigArr = m_AnalysisyConfig.commonCheckConfig.nodeConfigArr[0].regionConfigArr; - cv::Rect& cur_traditional_rect = m_pbaseCheckFunction->traditionDet.detArea_ROI; - std::vector& cur_traditional_region = m_pbaseCheckFunction->traditionDet.detArea; // if (img.empty()) // { @@ -733,9 +717,7 @@ int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, bool b_update, cv::Ro cur_regionConfigArr[i].basicInfo.pointArry[j] = affinePoint(m_old_cur_regionConfigArr[i].basicInfo.pointArry[j]); } } - for(int i = 0; i < cur_traditional_region.size(); i++){ - cur_traditional_region[i] = affinePoint(m_old_cur_traditional_region[i]); - } + /*show*/ // if (img.empty()) // { @@ -813,7 +795,7 @@ int ImgCheckAnalysisy::CheckRun() /*自适应更新参数*/ long time_adapt_s = CheckUtil::getcurTime(); cv::RotatedRect rot_cur_roi; - Adapt_Config(m_CheckResult_shareP->in_shareImage->img, m_CutRoi, m_pbaseCheckFunction->markLine.badapt_region, rot_cur_roi); + Adapt_Config(m_CheckResult_shareP->in_shareImage->img, m_CutRoi, m_pbaseCheckFunction->markLine.badapt_region); long time_adapt_e = CheckUtil::getcurTime(); m_pdetlog->AddCheckstr(PrintLevel_0, "Adapt_Config", "time = %ld", time_adapt_e - time_adapt_s); @@ -878,6 +860,7 @@ int ImgCheckAnalysisy::CheckRun() else { // 不推理:直接输出全黑残点图 + m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "-------------------------NO AI_Edge--------------- \n"); m_pImageAllResult->AIMaskImg = cv::Mat::zeros(m_pImageAllResult->detImg.size(), CV_8UC1); } @@ -2232,34 +2215,11 @@ int ImgCheckAnalysisy::Update_DetRoiList() float fx = m_pImageAllResult->fscale_detToresult_x; float fy = m_pImageAllResult->fscale_detToresult_y; - int num = 0; - cv::Rect offsetroi = m_Crop_Roi_paramImg; - - if (m_pEdge_Align_Result && m_pEdge_Align_Result->buseOfft) + // Adapt_Config 已通过仿射变换将配置区域适配到当前产品位置, + // 此处仅做坐标映射(减去裁剪ROI偏移 + 缩放),不再叠加边缘对齐的二次偏移 + for (const auto ®ion : m_pCommonAnalysisyConfig->regionConfigArr) { - for (const auto ®ion : m_pCommonAnalysisyConfig->regionConfigArr) - { - - if (m_pEdge_Align_Result->H.empty()) - { - - m_DetRoiList.Update_1(region.basicInfo.pointArry, offsetroi, m_pEdge_Align_Result->offt_x, m_pEdge_Align_Result->offt_y, fx, fy); - } - else - { - - m_DetRoiList.Update_Marit(region.basicInfo.pointArry, offsetroi, m_pEdge_Align_Result->H, fx, fy); - } - - // m_DetRoiList.Update_1(region.basicInfo.pointArry, offsetroi, m_pEdge_Align_Result->offt_x, m_pEdge_Align_Result->offt_y, fx, fy); - } - } - else - { - for (const auto ®ion : m_pCommonAnalysisyConfig->regionConfigArr) - { - m_DetRoiList.Update(region.basicInfo.pointArry, m_Crop_Roi_paramImg, fx, fy); - } + m_DetRoiList.Update(region.basicInfo.pointArry, m_Crop_Roi_paramImg, fx, fy); } m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "info", "roiList_Src roi num %ld", m_DetRoiList.roiList_Src.size()); @@ -2275,19 +2235,23 @@ int ImgCheckAnalysisy::DrawResult_Step_1() { int n = 0; // std::cout << m_DetRoiList.roiList_Show.size() << std::endl; - for (const auto &polygon : m_DetRoiList.roiList_Show) + for(int i = 0; i < m_DetRoiList.roiList_Show.size(); i++) { - // n++; - // if (n == 1) - // { - // continue; - // } + cv::polylines(m_CheckResult_shareP->resultimg, m_DetRoiList.roiList_Show[i], true, cv::Scalar(32, 128, (i*64+64)%255), 1); + } + // for (const auto &polygon : m_DetRoiList.roiList_Show) + // { + // // n++; + // // if (n == 1) + // // { + // // continue; + // // } - // std::cout << polygon << std::endl; + // // std::cout << polygon << std::endl; - // 绘制多边形的边界(不填充),使用绿色线条,线宽为2 - cv::polylines(m_CheckResult_shareP->resultimg, polygon, true, cv::Scalar(128, 128, 255), 1); // true表示闭合多边形 - } + // // 绘制多边形的边界(不填充),使用绿色线条,线宽为2 + // cv::polylines(m_CheckResult_shareP->resultimg, polygon, true, cv::Scalar(128, 128, 255), 1); // true表示闭合多边形 + // } // cv::imwrite(DetImgInfo_shareP->strChannel + "roi_src.png", m_pdetlog->temImgList[TEM_IMG_IDX_DrawSrc]); // cv::imwrite(DetImgInfo_shareP->strChannel + "roi_ss.png", m_pdetlog->temImgList[TEM_IMG_IDX_Result]); @@ -2302,13 +2266,17 @@ int ImgCheckAnalysisy::DrawResult_Step_1() { for (const auto &r : m_Edge_DetConfig.edge_det_roi) { - cv::Rect droi; - droi.x = r.roi.x * fs_resize_x; - droi.y = r.roi.y * fs_resize_y; - droi.width = r.roi.width * fs_resize_x; - droi.height = r.roi.height * fs_resize_y; - - cv::rectangle(m_CheckResult_shareP->resultimg, droi, cv::Scalar(255, 0, 255), 1); // 黄色角点 + cv::Point2f vertices[4]; + r.rrect.points(vertices); + for (int j = 0; j < 4; j++) + { + cv::Point p1, p2; + p1.x = (int)(vertices[j].x * fs_resize_x); + p1.y = (int)(vertices[j].y * fs_resize_y); + p2.x = (int)(vertices[(j+1)%4].x * fs_resize_x); + p2.y = (int)(vertices[(j+1)%4].y * fs_resize_y); + cv::line(m_CheckResult_shareP->resultimg, p1, p2, cv::Scalar(255, 0, 255), 1); + } } if (m_Edge_DetConfig.Det_region.size() > 2) @@ -2334,24 +2302,6 @@ int ImgCheckAnalysisy::DrawResult_Step_1() } } } - if (m_pbaseCheckFunction && m_pbaseCheckFunction->markLine.region.size() > 1) - { - std::vector product_roi_show; - for (const auto &pt : m_pbaseCheckFunction->markLine.region) - { - cv::Point draw_pt; - draw_pt.x = (pt.x - m_Crop_Roi_paramImg.x) * fs_resize_x; - draw_pt.y = (pt.y - m_Crop_Roi_paramImg.y) * fs_resize_y; - product_roi_show.push_back(draw_pt); - } - - cv::polylines( - m_CheckResult_shareP->resultimg, - product_roi_show, - true, - cv::Scalar(0, 255, 0), - 1); - } if (m_pEdge_Align_Result.get() != NULL) { @@ -2434,6 +2384,7 @@ int ImgCheckAnalysisy::Edge_Qx_Det(const cv::Mat &img) { m_Edge_DetConfig.Init(); + m_Edge_DetConfig.detlog = m_pdetlog; m_Edge_DetConfig.strChannel = m_strCurDetChannel; m_Edge_DetConfig.pBaseCheckFunction = m_pbaseCheckFunction; m_Edge_DetConfig.alginResult.corpRoi = m_Crop_Roi_paramImg; @@ -2656,11 +2607,12 @@ int ImgCheckAnalysisy::CalProductSize() m_CheckResult_shareP->productWidht_mm = fw; m_CheckResult_shareP->productHeight_mm = fh; - m_pdetlog->AddCheckstr(PrintLevel_1, "Det ROI", " [w %d,h %d] (piexl) -> [w %f h %f](mm),scale %f %f", - m_CutRoi.width, m_CutRoi.height, fw, fh, m_fImgage_Scale_X, m_fImgage_Scale_Y); + m_pdetlog->AddCheckstr(PrintLevel_1, "Det ROI", " [x%d, y%d, w %d,h %d] (piexl) -> [w %f h %f](mm),scale %f %f", + m_CutRoi.x, m_CutRoi.y, m_CutRoi.width, m_CutRoi.height, fw, fh, m_fImgage_Scale_X, m_fImgage_Scale_Y); return 0; } + int ImgCheckAnalysisy::ImgPreDet() { diff --git a/ConfigModule/src/JsonConfig.cpp b/ConfigModule/src/JsonConfig.cpp index a582c65..3a139a0 100644 --- a/ConfigModule/src/JsonConfig.cpp +++ b/ConfigModule/src/JsonConfig.cpp @@ -1197,6 +1197,10 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value) { _config.edgeDet.Det_Range = value_f["form"]["Detect_Config"]["Det_Range"].asInt(); } + if (value_f["form"]["Detect_Config"]["Det_Range_Offset"]) + { + _config.edgeDet.Det_Range_Offset = value_f["form"]["Detect_Config"]["Det_Range_Offset"].asInt(); + } if (value_f["form"]["Detect_Config"]["QX_Widht_min"]) {