From 0e72b46f87cd24cd516e7a4530a975576d728d39 Mon Sep 17 00:00:00 2001 From: liusiyang Date: Tue, 8 Sep 2026 09:07:09 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E4=BC=A0=E7=BB=9F?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E8=80=97=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TcsCheckModule/include/TcsCheck.h | 1 + TcsCheckModule/src/TcsCheck.cpp | 59 +++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/TcsCheckModule/include/TcsCheck.h b/TcsCheckModule/include/TcsCheck.h index 3a55f55..deb78d9 100644 --- a/TcsCheckModule/include/TcsCheck.h +++ b/TcsCheckModule/include/TcsCheck.h @@ -115,6 +115,7 @@ private: cv::Mat m_matBlur; cv::Mat m_matDraw; + cv::Rect m_rtCrop; // TraditionalDetect 中确定的裁剪区域(原图坐标),供分类结果坐标映射 cv::Size m_sizeImage; std::string m_strCurFile; diff --git a/TcsCheckModule/src/TcsCheck.cpp b/TcsCheckModule/src/TcsCheck.cpp index 0184a05..29713aa 100644 --- a/TcsCheckModule/src/TcsCheck.cpp +++ b/TcsCheckModule/src/TcsCheck.cpp @@ -223,10 +223,21 @@ void CTcsCheck::ClassifyBlobs(const cv::Mat& blurCrop, const cv::Mat& imgBlob) { m_vecDefectInfo.clear(); + // 分步计时:记录每个步骤的耗时(ms) + auto tStart = std::chrono::high_resolution_clock::now(); + auto tPrev = tStart; + auto logStep = [&](const std::string& step) { + auto tNow = std::chrono::high_resolution_clock::now(); + double elapsedMs = std::chrono::duration_cast(tNow - tPrev).count(); + std::cout << "[ClassifyBlobs] " << step << " 耗时(ms): " << elapsedMs << std::endl; + tPrev = tNow; + }; + cv::Mat labels; cv::Mat stats; cv::Mat centroids; const int nLabels = cv::connectedComponentsWithStats(imgBlob, labels, stats, centroids, 8, CV_32S); + // logStep("1.连通域分析"); if (nLabels <= 1) return; struct BlobItem { int label; int area; }; @@ -238,11 +249,13 @@ void CTcsCheck::ClassifyBlobs(const cv::Mat& blurCrop, const cv::Mat& imgBlob) blobs.push_back({ label, area }); } } + // logStep("2.面积过滤"); if (blobs.empty()) return; std::sort(blobs.begin(), blobs.end(), [](const BlobItem& a, const BlobItem& b) { return a.area > b.area; }); + // logStep("3.面积排序"); // 大块缺陷的面积阈值 const int largeAreaThreshold = std::max(1, m_cpCfg.nBlockSize * m_cpCfg.nBlockSize / 4); @@ -307,6 +320,14 @@ void CTcsCheck::ClassifyBlobs(const cv::Mat& blurCrop, const cv::Mat& imgBlob) info.strDefectDesc = DEFECT_TYPE_DESC[defectType]; m_vecDefectInfo.push_back(info); } + // logStep("4.缺陷分类决策"); + + // // 总耗时 + // { + // auto tEnd = std::chrono::high_resolution_clock::now(); + // double totalMs = std::chrono::duration_cast(tEnd - tStart).count(); + // std::cout << "[ClassifyBlobs] 总耗时(ms): " << totalMs << std::endl; + // } } // ============================================================ @@ -421,6 +442,8 @@ int CTcsCheck::TraditionalDetect(const cv::Mat& img, cv::Rect detRoi, cv::Mat& b return -1; } } + // 缓存裁剪区域,供 TraditionalClassify 将小图坐标映射回原图 + m_rtCrop = rtCrop; // logStep("4.安全裁剪"); cv::Mat matCrop = m_matLoad(rtCrop); @@ -468,17 +491,41 @@ int CTcsCheck::TraditionalDetect(const cv::Mat& img, cv::Rect detRoi, cv::Mat& b // ============================================================ // TraditionalClassify — 独立分类接口 -// 对残点二值图做连通域分析+缺陷分类决策树 -// 前提: 已调用 TraditionalDetect(blobImg 已与 m_matLoad 同尺寸) -// 使用没有任何处理的原图 m_matLoad 计算灰阶差 +// 优先在小图缓存(m_matBlur/m_matBlob)上做连通域分析+缺陷分类, +// 再将结果坐标映射回原图,避免对全尺寸 mask 做昂贵计算。 // 返回: 分类到的缺陷数量,<0 表示异常 // ============================================================ int CTcsCheck::TraditionalClassify(const cv::Mat& blobImg) { - if (m_matLoad.empty() || blobImg.empty()) return -1; + // 全尺寸 mask 仅是小图 INTER_NEAREST 放大结果,连通域数量/形状完全等价, + // 在小图上分类可避免对全尺寸图做昂贵的 connectedComponentsWithStats。 + if (m_matBlur.empty() || m_matBlob.empty()) + { + // 回退:无小图缓存时,退化为全尺寸分类(旧行为) + if (m_matLoad.empty() || blobImg.empty()) return -1; + ClassifyBlobs(m_matLoad, blobImg); + return static_cast(m_vecDefectInfo.size()); + } + + auto tStart = std::chrono::high_resolution_clock::now(); + + ClassifyBlobs(m_matBlur, m_matBlob); - // 不再为全尺寸 mask 做一次 clone,直接使用 blobImg 分类 - ClassifyBlobs(m_matLoad, blobImg); + // 小图坐标/面积映射回原图坐标系,与下游 blobs.blobTab(全尺寸坐标)对齐 + const double scaleX = (m_matBlob.cols > 0) ? static_cast(m_rtCrop.width) / m_matBlob.cols : 1.0; + const double scaleY = (m_matBlob.rows > 0) ? static_cast(m_rtCrop.height) / m_matBlob.rows : 1.0; + for (auto& info : m_vecDefectInfo) + { + info.nDefectX = static_cast(info.nDefectX * scaleX + 0.5) + m_rtCrop.x; + info.nDefectY = static_cast(info.nDefectY * scaleY + 0.5) + m_rtCrop.y; + info.nDefectWidth = std::max(1, static_cast(info.nDefectWidth * scaleX + 0.5)); + info.nDefectHeight = std::max(1, static_cast(info.nDefectHeight * scaleY + 0.5)); + info.nDefectArea = std::max(1, static_cast(info.nDefectArea * scaleX * scaleY + 0.5)); + } + + auto tEnd = std::chrono::high_resolution_clock::now(); + double totalMs = std::chrono::duration_cast(tEnd - tStart).count(); + std::cout << "[TraditionalClassify] 总耗时(ms): " << totalMs << std::endl; return static_cast(m_vecDefectInfo.size()); }