diff --git a/TcsCheckModule/include/TcsCheck.h b/TcsCheckModule/include/TcsCheck.h index 93fd1af..db7b285 100644 --- a/TcsCheckModule/include/TcsCheck.h +++ b/TcsCheckModule/include/TcsCheck.h @@ -108,7 +108,7 @@ private: cv::Mat m_matLoad; cv::Mat m_matBlob; - cv::Mat m_matBlur; // 模糊图,供 TraditionalClassify 使用 + cv::Mat m_matBlur; cv::Mat m_matDraw; cv::Size m_sizeImage; diff --git a/TcsCheckModule/src/TcsCheck.cpp b/TcsCheckModule/src/TcsCheck.cpp index 9304a51..f8a53ae 100644 --- a/TcsCheckModule/src/TcsCheck.cpp +++ b/TcsCheckModule/src/TcsCheck.cpp @@ -225,9 +225,9 @@ void CTcsCheck::ClassifyBlobs(const cv::Mat& blurCrop, const cv::Mat& imgBlob) const int w = stats.at(label, cv::CC_STAT_WIDTH); const int h = stats.at(label, cv::CC_STAT_HEIGHT); - // blob 区域均值 - cv::Mat blobMask = (labels == label); - double blobMean = cv::mean(blurCrop, blobMask)[0]; + // blob 区域均值:仅在 bounding rect 局部计算,避免全图 mask + cv::Mat blobMask = (labels(cv::Rect(x, y, w, h)) == label); + double blobMean = cv::mean(blurCrop(cv::Rect(x, y, w, h)), blobMask)[0]; // 所属局部块均值 const double cx = centroids.at(label, 0); @@ -338,6 +338,16 @@ int CTcsCheck::TraditionalDetect(const cv::Mat& img, cv::Rect detRoi, cv::Mat& b return -1; } + // 分步计时:记录每个步骤的耗时(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 << "[TraditionalDetect] " << step << " 耗时(ms): " << elapsedMs << std::endl; + tPrev = tNow; + }; + // getchar(); // cv::Mat showImg = img.clone(); // cv::rectangle(showImg, detRoi, cv::Scalar(255), 2); @@ -352,6 +362,7 @@ int CTcsCheck::TraditionalDetect(const cv::Mat& img, cv::Rect detRoi, cv::Mat& b // 1. 全局阈值 → 产品区域定位 cv::Mat matBinary; cv::threshold(m_matLoad, matBinary, m_cpCfg.nAreaLowFilter, 255, cv::THRESH_BINARY); + // logStep("1.全局阈值"); // 2. 获取最大连通域外接矩形 cv::Rect rtValid = GetBoundingRect(matBinary); @@ -360,9 +371,11 @@ int CTcsCheck::TraditionalDetect(const cv::Mat& img, cv::Rect detRoi, cv::Mat& b blobImg = cv::Mat(); return -1; } + // logStep("2.最大连通域外接矩形"); // 3. 裁剪边缘 rtCrop = GetCropArea(rtValid); + // logStep("3.裁剪边缘"); } // 安全裁剪: 确保 rtCrop 不超出 m_matLoad 边界 @@ -375,53 +388,46 @@ int CTcsCheck::TraditionalDetect(const cv::Mat& img, cv::Rect detRoi, cv::Mat& b return -1; } } + // logStep("4.安全裁剪"); - cv::Mat matCrop = m_matLoad(rtCrop).clone(); + cv::Mat matCrop = m_matLoad(rtCrop); + // logStep("5.ROI裁剪"); - // 4. 缩放 + // 6. 缩放 const int outW = std::max(1, static_cast(matCrop.cols * m_cpCfg.fZoomRatio)); const int outH = std::max(1, static_cast(matCrop.rows * m_cpCfg.fZoomRatio)); cv::Mat matResized; cv::resize(matCrop, matResized, cv::Size(outW, outH), 0, 0, cv::INTER_AREA); + // logStep("6.缩放"); - // 5. 高斯模糊 — 缓存到 m_matBlur + // 7. 高斯模糊 — 缓存到 m_matBlur cv::GaussianBlur(matResized, m_matBlur, cv::Size(5, 5), 0); // cv::imwrite("matResized.png", matResized); // cv::imwrite("m_matBlur.png", m_matBlur); - // 6. 自适应二值化检测 + // logStep("7.高斯模糊"); + + // 8. 自适应二值化检测 m_matBlob = AdaptiveBinary(m_matBlur); - blobImg = m_matBlob.clone(); + blobImg = m_matBlob; + // logStep("8.自适应二值化"); - // 7. 反向映射:将 crop+resize 后的残点图恢复为原始输入图尺寸 - // 调用方拿到的是与输入 img 同尺寸的 mask,坐标体系一致 + // 9. 反向映射:只生成下游需要的全尺寸残点 mask(resultMaskImg / Blob 计算)。 + // 直接 resize 到 ROI 上,省去中间 matCropSize 缓冲与 copyTo; + // m_matBlob 保持小图即可,全尺寸副本无任何消费者,已移除。 if (m_cpCfg.fZoomRatio > 0) { - // 7a. 反向映射 blobImg(二值 mask,用 INTER_NEAREST) - { - cv::Mat matCropSize; - cv::resize(blobImg, matCropSize, cv::Size(rtCrop.width, rtCrop.height), 0, 0, cv::INTER_NEAREST); - cv::Mat fullMask = cv::Mat::zeros(m_matLoad.size(), CV_8UC1); - matCropSize.copyTo(fullMask(rtCrop)); - blobImg = fullMask; - } + cv::Mat fullMask = cv::Mat::zeros(m_matLoad.size(), CV_8UC1); + cv::resize(m_matBlob, fullMask(rtCrop), cv::Size(rtCrop.width, rtCrop.height), 0, 0, cv::INTER_NEAREST); + blobImg = fullMask; - // 7b. 同步反向映射 m_matBlur(灰度图,供 TraditionalClassify 使用,用 INTER_LINEAR) - { - cv::Mat matCropSize; - cv::resize(m_matBlur, matCropSize, cv::Size(rtCrop.width, rtCrop.height), 0, 0, cv::INTER_LINEAR); - cv::Mat fullBlur = cv::Mat::zeros(m_matLoad.size(), m_matBlur.type()); - matCropSize.copyTo(fullBlur(rtCrop)); - m_matBlur = fullBlur; - } + // logStep("9.反向映射(mask)"); + } - // 7c. 同步反向映射 m_matBlob(二值 mask,用 INTER_NEAREST) - { - cv::Mat matCropSize; - cv::resize(m_matBlob, matCropSize, cv::Size(rtCrop.width, rtCrop.height), 0, 0, cv::INTER_NEAREST); - cv::Mat fullBlob = cv::Mat::zeros(m_matLoad.size(), CV_8UC1); - matCropSize.copyTo(fullBlob(rtCrop)); - m_matBlob = fullBlob; - } + // 总耗时 + { + auto tEnd = std::chrono::high_resolution_clock::now(); + double totalMs = std::chrono::duration_cast(tEnd - tStart).count(); + // std::cout << "[TraditionalDetect] 总耗时(ms): " << totalMs << std::endl; } return 0; @@ -430,15 +436,16 @@ int CTcsCheck::TraditionalDetect(const cv::Mat& img, cv::Rect detRoi, cv::Mat& b // ============================================================ // TraditionalClassify — 独立分类接口 // 对残点二值图做连通域分析+缺陷分类决策树 -// 前提: 已调用 TraditionalDetect(m_matBlur 已缓存) +// 前提: 已调用 TraditionalDetect(blobImg 已与 m_matLoad 同尺寸) +// 使用没有任何处理的原图 m_matLoad 计算灰阶差 // 返回: 分类到的缺陷数量,<0 表示异常 // ============================================================ int CTcsCheck::TraditionalClassify(const cv::Mat& blobImg) { - if (m_matBlur.empty() || blobImg.empty()) return -1; + if (m_matLoad.empty() || blobImg.empty()) return -1; - m_matBlob = blobImg.clone(); - ClassifyBlobs(m_matBlur, blobImg); + // 不再为全尺寸 mask 做一次 clone,直接使用 blobImg 分类 + ClassifyBlobs(m_matLoad, blobImg); return static_cast(m_vecDefectInfo.size()); }