diff --git a/AlgorithmModule/include/ALLImgCheckAnalysisy.hpp b/AlgorithmModule/include/ALLImgCheckAnalysisy.hpp index 877c505..dc5bd28 100644 --- a/AlgorithmModule/include/ALLImgCheckAnalysisy.hpp +++ b/AlgorithmModule/include/ALLImgCheckAnalysisy.hpp @@ -67,6 +67,8 @@ public: // 获取模板外接矩形 const cv::RotatedRect &GetTplOuterRect() const { return m_tplOuterRect; } + // 获取模板内边缘矩形 + const cv::RotatedRect &GetTplInnerRect() const { return m_tplInnerRect; } // 获取模板 pin blob 轮廓点集 const std::vector> &GetTplPinContours() const { return m_tplPinContours; } @@ -180,6 +182,7 @@ private: // 模板信息 cv::RotatedRect m_tplOuterRect; // 模板外接矩形 + cv::RotatedRect m_tplInnerRect; // 模板内边缘矩形 std::vector> m_tplPinContours; // 模板 pin blob 轮廓点集 }; diff --git a/AlgorithmModule/include/CameraCheckAnalysisy.hpp b/AlgorithmModule/include/CameraCheckAnalysisy.hpp index 10429e1..b80ce21 100644 --- a/AlgorithmModule/include/CameraCheckAnalysisy.hpp +++ b/AlgorithmModule/include/CameraCheckAnalysisy.hpp @@ -135,6 +135,7 @@ public: // 设置模板信息并转发给各个检测核心 void SetTplInfo(const cv::RotatedRect &outerRect, + const cv::RotatedRect &innerRect, const std::vector> &pinContours); private: diff --git a/AlgorithmModule/include/ImgCheckAnalysisy.hpp b/AlgorithmModule/include/ImgCheckAnalysisy.hpp index b9b62c4..ab45249 100644 --- a/AlgorithmModule/include/ImgCheckAnalysisy.hpp +++ b/AlgorithmModule/include/ImgCheckAnalysisy.hpp @@ -80,8 +80,9 @@ public: int creatsavedir(); - // 设置模板信息(模板外接矩形 + 模板 pin blob 轮廓点集) + // 设置模板信息(模板外接矩形 + 模板内边缘矩形 + 模板 pin blob 轮廓点集) void SetTplInfo(const cv::RotatedRect &outerRect, + const cv::RotatedRect &innerRect, const std::vector> &pinContours); private: @@ -201,8 +202,8 @@ private: // 获取指定 残点(blob) 是由哪个(哪些)检测模型检出的 std::string GetBlobModelName(const cv::Rect &roi); - //自适应参数区域更新 - int Adapt_Config(cv::RotatedRect tplOuterRect, cv::RotatedRect curOuterRect, cv::Mat& det_img); + //自适应参数区域更新:仿射变换到模板坐标系(基准矩形可为内定位/外定位) + int Adapt_Config(cv::RotatedRect tplRefRect, cv::RotatedRect curRefRect, cv::Mat& det_img); std::shared_ptr AI_Factory; @@ -299,6 +300,7 @@ private: cv::RotatedRect m_tplOuterRect; // 模板外接矩形 cv::RotatedRect m_outer_rroi; cv::RotatedRect m_inner_rroi; + cv::RotatedRect m_tplInnerRect; // 模板内边缘矩形 std::vector> m_tplPinContours; // 模板 pin blob 轮廓点集 std::vector> m_curPinContours; // 当前 pin blob 轮廓点集 }; diff --git a/AlgorithmModule/src/AI_Edge_Algin.cpp b/AlgorithmModule/src/AI_Edge_Algin.cpp index df10acb..b9451c7 100644 --- a/AlgorithmModule/src/AI_Edge_Algin.cpp +++ b/AlgorithmModule/src/AI_Edge_Algin.cpp @@ -246,7 +246,8 @@ int AI_Edge_Algin::Detect(const cv::Mat &img, DetConfig *pDetConfig, std::shared } return re; } - cv::Mat Big_outimg_src = img(Big_roi.boundingRect()).clone(); + cv::Rect Big_out_roi = Big_roi.boundingRect() & cv::Rect(0, 0, img.cols, img.rows); + cv::Mat Big_outimg_src = img(Big_out_roi).clone(); re = Get_Edge(1, Big_outimg_src, pDetConfig, m_pDetConfig->strChannel, Small_roi); if (re != 0) { diff --git a/AlgorithmModule/src/ALLImgCheckAnalysisy.cpp b/AlgorithmModule/src/ALLImgCheckAnalysisy.cpp index 00e7387..a4f9e52 100644 --- a/AlgorithmModule/src/ALLImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ALLImgCheckAnalysisy.cpp @@ -101,7 +101,7 @@ int ALLImgCheckAnalysisy::RunStart(void *pconfig1) { if (item.second) { - item.second->SetTplInfo(m_tplOuterRect, m_tplPinContours); + item.second->SetTplInfo(m_tplOuterRect, m_tplInnerRect, m_tplPinContours); } } m_nErrorCode = CHECK_OK; @@ -172,7 +172,7 @@ int ALLImgCheckAnalysisy::UpdateConfig(void *pconfig, int nConfigType) { if (item.second) { - item.second->SetTplInfo(m_tplOuterRect, m_tplPinContours); + item.second->SetTplInfo(m_tplOuterRect, m_tplInnerRect, m_tplPinContours); } } } @@ -249,7 +249,7 @@ int ALLImgCheckAnalysisy::InitAIFactory() int ALLImgCheckAnalysisy::InitTemplateMask() { std::shared_ptr AI_Factory = AIFactory::GetInstance(); - if (!AI_Factory || !AI_Factory->Align_Outer || !AI_Factory->Pin_Loc) + if (!AI_Factory || !AI_Factory->Align_Outer || !AI_Factory->Align_Inner || !AI_Factory->Pin_Loc) { return -1; } @@ -321,6 +321,78 @@ int ALLImgCheckAnalysisy::InitTemplateMask() return 6; } tplOuterRect = cv::minAreaRect(srcContour); + + // 模板内边缘矩形,通过模型Align_Inner得到 + // 与 AI_Edge_Algin::Detect 的内定位流程保持一致:按外接矩形裁剪模板图 -> Align_Inner 推理 -> 轮廓还原并把裁剪偏移加回,得到模板图坐标系下的内边缘矩形 + // PIN 偏移/缺失检测的仿射变换以内定位为基准,模板侧需先准备好该矩形;计算失败时保持空值,运行时自动回退外定位 + cv::RotatedRect tplInnerRect; + bool bTplInnerOk = false; + std::shared_ptr pSocket_AlignInner = AI_Factory->Align_Inner; + cv::Rect tplOuterBound = tplOuterRect.boundingRect() & cv::Rect(0, 0, tplImg.cols, tplImg.rows); + if (pSocket_AlignInner && tplOuterBound.width > 0 && tplOuterBound.height > 0) + { + cv::Mat innerCropImg = tplImg(tplOuterBound).clone(); + cv::Size innerSz; + innerSz.width = pSocket_AlignInner->input_0.width; + innerSz.height = pSocket_AlignInner->input_0.height; + cv::Mat innerDetImg; + cv::resize(innerCropImg, innerDetImg, innerSz); + if (innerDetImg.channels() != 1) + { + // 与运行时 AI_Edge_Algin::Get_Edge 内定位的灰度化保持一致(BGR 顺序) + cv::cvtColor(innerDetImg, innerDetImg, cv::COLOR_BGR2GRAY); + } + cv::Mat innerMask; + int reInner = pSocket_AlignInner->AIDet(innerDetImg, innerMask); + if (reInner != 0) + { + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "AICheck_Inner----error %d ", reInner); + } + else + { + // 找到最大轮廓 + bool innerFound = false; + std::vector innerBoundingBox = CheckUtil::getLargestContourROI(innerMask, innerFound); + if (!innerFound || innerBoundingBox.size() < 3) + { + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "Inner align: No contours found!----error "); + } + else + { + // 内定位小图 到 裁剪图 的缩放比例 + const float inner_scale_x = static_cast(innerCropImg.cols) / innerSz.width; + const float inner_scale_y = static_cast(innerCropImg.rows) / innerSz.height; + std::vector innerContour; + innerContour.reserve(innerBoundingBox.size()); + for (const auto &pt : innerBoundingBox) + { + innerContour.emplace_back(pt.x * inner_scale_x, pt.y * inner_scale_y); + } + tplInnerRect = cv::minAreaRect(innerContour); + // 裁剪图坐标 -> 模板图坐标(与 AI_Edge_Algin::Detect 中 smallroi 偏移加回一致) + tplInnerRect.center.x += static_cast(tplOuterBound.x); + tplInnerRect.center.y += static_cast(tplOuterBound.y); + bTplInnerOk = true; + } + } + } + else + { + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "Inner align: model null or outer bound invalid!----error "); + } + if (bTplInnerOk) + { + m_tplInnerRect = tplInnerRect; + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", + "tplInnerRect center(%.1f %.1f) size(%.1f %.1f) angle %.1f", + tplInnerRect.center.x, tplInnerRect.center.y, + tplInnerRect.size.width, tplInnerRect.size.height, tplInnerRect.angle); + } + else + { + m_tplInnerRect = cv::RotatedRect(); + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "Tpl Inner align invalid, runtime fallback to Outer align!"); + } // 模板Pinmask,通过模型Pin_Loc得到 Mat tplPinMask; // 模板mask diff --git a/AlgorithmModule/src/CameraCheckAnalysisy.cpp b/AlgorithmModule/src/CameraCheckAnalysisy.cpp index a630975..a35f74d 100644 --- a/AlgorithmModule/src/CameraCheckAnalysisy.cpp +++ b/AlgorithmModule/src/CameraCheckAnalysisy.cpp @@ -629,6 +629,7 @@ int CameraCheckAnalysisy::Init(std::string strcameraName) } void CameraCheckAnalysisy::SetTplInfo(const cv::RotatedRect &outerRect, + const cv::RotatedRect &innerRect, const std::vector> &pinContours) { for (int i = 0; i < IMGCHECKANALYSISY_NUM; i++) @@ -636,7 +637,7 @@ void CameraCheckAnalysisy::SetTplInfo(const cv::RotatedRect &outerRect, auto *p = static_cast(m_pImgCheckAnalysisy[i]); if (p) { - p->SetTplInfo(outerRect, pinContours); + p->SetTplInfo(outerRect, innerRect, pinContours); } } } diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index baca252..0bfe64a 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -211,9 +211,11 @@ int ImgCheckAnalysisy::creatsavedir() } void ImgCheckAnalysisy::SetTplInfo(const cv::RotatedRect &outerRect, + const cv::RotatedRect &innerRect, const std::vector> &pinContours) { m_tplOuterRect = outerRect; + m_tplInnerRect = innerRect; m_tplPinContours = pinContours; } @@ -370,9 +372,10 @@ cv::Scalar ImgCheckAnalysisy::calc_blob_info_withstats(cv::Mat &img, const cv::M return cv::Scalar(worb, energy, hj); } -int ImgCheckAnalysisy::Adapt_Config(RotatedRect tplOuterRect, RotatedRect curOuterRect, Mat& det_img){ - vector cur_vertices = CheckUtil::sort_vertices(curOuterRect); - vector tpl_vertices = CheckUtil::sort_vertices(tplOuterRect); +// 仿射变换到模板坐标系:把当前基准矩形(内定位/外定位)的顶点映射到模板对应基准矩形上 +int ImgCheckAnalysisy::Adapt_Config(RotatedRect tplRefRect, RotatedRect curRefRect, Mat& det_img){ + vector cur_vertices = CheckUtil::sort_vertices(curRefRect); + vector tpl_vertices = CheckUtil::sort_vertices(tplRefRect); cv::Point2f src_pts[3] = { cur_vertices[0], // 对应左上 @@ -554,7 +557,21 @@ int ImgCheckAnalysisy::CheckRun() } /*投影对齐模板*/ - Adapt_Config(m_tplOuterRect, m_outer_rroi, m_CheckResult_shareP->in_shareImage->img); + // 检测PIN偏移/缺失对位置基准最敏感:PIN 位置的真实基准是产品内腔(内定位), + // 因此优先用内定位做仿射变换(当前内边缘 -> 模板内边缘); + // 模板内定位/当前内定位缺失或无效时,回退到外定位(与旧行为一致) + bool bUseInnerAlign = (m_tplInnerRect.size.width > 0 && m_tplInnerRect.size.height > 0 && + m_inner_rroi.size.width > 0 && m_inner_rroi.size.height > 0); + if (bUseInnerAlign) + { + m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "Affine align basis: Inner(inner_rroi -> tplInnerRect)"); + Adapt_Config(m_tplInnerRect, m_inner_rroi, m_CheckResult_shareP->in_shareImage->img); + } + else + { + m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "Affine align basis: Outer(outer_rroi -> tplOuterRect)"); + Adapt_Config(m_tplOuterRect, m_outer_rroi, m_CheckResult_shareP->in_shareImage->img); + } m_outer_roi = m_tplOuterRect.boundingRect(); m_Crop_Roi_paramImg = m_outer_roi; m_pImageAllResult->pDetResult->CutRoi = m_outer_roi;