update 优化传统检测

dev_lsy
liusiyang 2 weeks ago
parent ae6cfc95c1
commit 7aca87a0bf

@ -108,7 +108,7 @@ private:
cv::Mat m_matLoad; cv::Mat m_matLoad;
cv::Mat m_matBlob; cv::Mat m_matBlob;
cv::Mat m_matBlur; // 模糊图,供 TraditionalClassify 使用 cv::Mat m_matBlur;
cv::Mat m_matDraw; cv::Mat m_matDraw;
cv::Size m_sizeImage; cv::Size m_sizeImage;

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

Loading…
Cancel
Save