diff --git a/AlgorithmModule/CMakeLists.txt b/AlgorithmModule/CMakeLists.txt index f6299c7..f25d151 100644 --- a/AlgorithmModule/CMakeLists.txt +++ b/AlgorithmModule/CMakeLists.txt @@ -46,6 +46,7 @@ ${PROJECT_SOURCE_DIR}/ConfigModule/include ${PROJECT_SOURCE_DIR}/Common/include ${PROJECT_SOURCE_DIR}/AIEngineModule/include ${PROJECT_SOURCE_DIR}/AIEngineModule/include_base +${PROJECT_SOURCE_DIR}/TcsCheckModule/include ) link_directories( /usr/local/lib/ @@ -67,6 +68,7 @@ add_library(TY_Check SHARED ${SRC_LISTS}) target_link_libraries(TY_Check nvinfer Config + TcsCheck ${OpenCV_LIBS} ${CUDA_LIBRARIES} ) diff --git a/AlgorithmModule/include/ImgCheckAnalysisy.hpp b/AlgorithmModule/include/ImgCheckAnalysisy.hpp index a4f754d..b9acbe5 100644 --- a/AlgorithmModule/include/ImgCheckAnalysisy.hpp +++ b/AlgorithmModule/include/ImgCheckAnalysisy.hpp @@ -36,6 +36,7 @@ #include "AI_Factory.h" #include "ImageAllResult.h" #include "Task.h" +#include "TcsCheck.h" using namespace std; using namespace cv; @@ -263,6 +264,9 @@ private: Edge_QX_Det::DetConfigResult m_Edge_DetConfig; std::vector m_Draw_qxImageResult; // 缺陷小图结果 + // 传统检测模块 + CTcsCheck m_tcsCheck; + std::vector SmallRoiList; std::string m_strRootPath_TA_cls; diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 0506c30..3450871 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -1611,14 +1611,33 @@ int ImgCheckAnalysisy::Traditional_Detect_Thread(const cv::Mat &img, cv::Mat &Re std::string strBaseLog = "Traditional_Detect"; m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "Traditional_Detect Start"); - // 输出与AI推理相同格式的二值mask图 (CV_8UC1) - ResultImg = cv::Mat::zeros(img.size(), CV_8UC1); - - // ===== TODO: 在此处调用传统检测算法库 ===== - // 示例:cv::threshold(img, ResultImg, 128, 255, cv::THRESH_BINARY); - // 输入: img (单通道灰度图, CV_8UC1) - // 输出: ResultImg (二值mask, CV_8UC1, 0/255) - // ========================================== + // 首次调用时初始化传统检测参数(从 m_AnalysisyConfig 映射) + static bool bTcsInited = false; + if (!bTcsInited) + { + CHECK_PARAM cp; + cp.nAreaLowFilter = 80; + cp.nBlockSize = 100; + cp.nDiscardTop = 170; + cp.nDiscardBottom = 170; + cp.nDiscardLeft = 180; + cp.nDiscardRight = 460; + cp.fZoomRatio = 0.25f; + cp.nFilterLow = 15; + cp.nFilterHigh = 15; + cp.nAreaFilter = 10; + cp.nCountFilter = 50; + m_tcsCheck.SetChecConfig(&cp); + bTcsInited = true; + } + + // 调用传统检测:输出残点二值图 + int ret = m_tcsCheck.TraditionalDetect(img, ResultImg); + if (ret != 0) + { + m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "Traditional_Detect FAILED (no product)"); + return -1; + } m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "Traditional_Detect End"); return 0; @@ -1792,8 +1811,32 @@ int ImgCheckAnalysisy::AI_QX_Class_Thread() // ======================== 传统分类 ======================== int ImgCheckAnalysisy::Traditional_QX_Class_Thread() { - m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Traditional_Class", " Start (placeholder)"); + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Traditional_Class", " Start"); + + // 1. 调用传统分类:基于上次 TraditionalDetect 缓存的模糊图 + 当前 mask + if (m_pImageAllResult == nullptr || m_pImageAllResult->AIMaskImg.empty()) + { + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Traditional_Class", " No mask image"); + return -1; + } + + int nDefectCount = m_tcsCheck.TraditionalClassify(m_pImageAllResult->AIMaskImg); + if (nDefectCount < 0) + { + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Traditional_Class", " Classify failed"); + return -1; + } + + // 2. TcsCheck 缺陷类型 → CONFIG_QX_NAME 映射表 + static const int TcsDefectToConfigQX[] = { + CONFIG_QX_NAME_cell_other, // DEFECT_TYPE_OK = 0 + CONFIG_QX_NAME_cell_dianzhuang, // DEFECT_TYPE_POINT = 1 (硬质颗粒 → 点状) + CONFIG_QX_NAME_cell_line, // DEFECT_TYPE_SCRATCH = 2 (划伤 → 线状) + CONFIG_QX_NAME_cell_zangwu, // DEFECT_TYPE_DIRTY = 3 (脏污) + CONFIG_QX_NAME_cell_danban, // DEFECT_TYPE_FADING_SPOTS = 4 (淡斑) + }; + // 3. 将分类结果匹配到 blobs.blobTab(基于位置/面积最近邻匹配) int totalTasks = blobs.blobCount; for (int i = 0; i < totalTasks; i++) { @@ -1802,19 +1845,45 @@ int ImgCheckAnalysisy::Traditional_QX_Class_Thread() if (pblob->ErrType == ERR_TYPE_2) { pblob->AIclasstype = CONFIG_QX_NAME_cell_ymhs; + continue; + } + + // 在 TcsCheck 结果中找最佳匹配(中心距离最近) + int bestIdx = -1; + int bestDist2 = INT_MAX; + int blobCenterX = (pblob->minx + pblob->maxx) / 2; + int blobCenterY = (pblob->miny + pblob->maxy) / 2; + + for (int j = 0; j < nDefectCount; j++) + { + const DEFECT_INFO& info = m_tcsCheck.m_vecDefectInfo[j]; + int defCenterX = info.nDefectX + info.nDefectWidth / 2; + int defCenterY = info.nDefectY + info.nDefectHeight / 2; + int dx = blobCenterX - defCenterX; + int dy = blobCenterY - defCenterY; + int dist2 = dx * dx + dy * dy; + + // 面积接近的优先(容差 50% 内) + int areaDiff = std::abs(pblob->area - info.nDefectArea); + if (areaDiff < info.nDefectArea / 2 && dist2 < bestDist2) + { + bestDist2 = dist2; + bestIdx = j; + } + } + + if (bestIdx >= 0) + { + int defectType = m_tcsCheck.m_vecDefectInfo[bestIdx].nDefectType; + pblob->AIclasstype = TcsDefectToConfigQX[defectType]; } else { - // ===== TODO: 在此处调用传统分类算法库 ===== - // 输入: pblob->minx/maxy/miny/maxy 定位的blob区域 - // m_pImageAllResult->detImg 原始检测图 - // 输出: pblob->AIclasstype (CONFIG_QX_NAME_cell_xxx) - // ========================================== pblob->AIclasstype = CONFIG_QX_NAME_cell_other; } } - m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Traditional_Class", " End (placeholder), classified %d blobs", totalTasks); + m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Traditional_Class", " End, classified %d/%d blobs", nDefectCount, totalTasks); return 0; } diff --git a/CMakeLists.txt b/CMakeLists.txt index 1808a0e..18d6197 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ message(STATUS "oPENCV Library status:") message(STATUS ">version:${OpenCV_VERSION}") message(STATUS "Include:${OpenCV_INCLUDE_DIRS}") -set(CMAKE_BUILD_TYPE "debug") +set(CMAKE_BUILD_TYPE "release") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 要求编译器支持 C++17 set(CMAKE_CXX_EXTENSIONS OFF) # 禁用 GNU 特定的扩展 diff --git a/TcsCheckModule/include/TcsCheck.h b/TcsCheckModule/include/TcsCheck.h index a214a2b..df33bb4 100644 --- a/TcsCheckModule/include/TcsCheck.h +++ b/TcsCheckModule/include/TcsCheck.h @@ -95,6 +95,15 @@ public: void SetChecConfig(CHECK_PARAM* cp); void ProcessImages(bool bDrawResult); + // ========== 独立检测/分类接口 ========== + // 传统检测:对单张图做完整预处理+自适应二值化,输出残点二值图 + // 返回: 0=成功, -1=无产品/输入为空 + int TraditionalDetect(const cv::Mat& img, cv::Mat& blobImg); + // 传统分类:对残点二值图做连通域分析+缺陷分类,结果写入 m_vecDefectInfo + // 前提: 已调用 TraditionalDetect(内部 m_matBlur 已就绪) + // 返回: 分类到的缺陷数量,<0 表示异常 + int TraditionalClassify(const cv::Mat& blobImg); + private: std::string m_strDirIn; @@ -102,6 +111,7 @@ private: cv::Mat m_matLoad; cv::Mat m_matBlob; + cv::Mat m_matBlur; // 模糊图,供 TraditionalClassify 使用 cv::Mat m_matDraw; cv::Size m_sizeImage; diff --git a/TcsCheckModule/src/TcsCheck.cpp b/TcsCheckModule/src/TcsCheck.cpp index b4aea37..dcaf55b 100644 --- a/TcsCheckModule/src/TcsCheck.cpp +++ b/TcsCheckModule/src/TcsCheck.cpp @@ -309,6 +309,67 @@ cv::Mat CTcsCheck::DrawBlobInfoImage(const cv::Mat& imgCrop, const cv::Mat& imgB return cropColor; } +// ============================================================ +// TraditionalDetect — 独立检测接口 +// 对单张图做:阈值定位→裁剪→缩放→高斯模糊→自适应二值化 +// 输出残点二值图 blobImg,内部缓存 m_matBlur 供后续分类 +// 返回: 0=成功, -1=无产品或输入异常 +// ============================================================ +int CTcsCheck::TraditionalDetect(const cv::Mat& img, cv::Mat& blobImg) +{ + if (img.empty()) return -1; + + m_matLoad = img; + m_sizeImage = img.size(); + + // 1. 全局阈值 → 产品区域定位 + cv::Mat matBinary; + cv::threshold(m_matLoad, matBinary, m_cpCfg.nAreaLowFilter, 255, cv::THRESH_BINARY); + + // 2. 获取最大连通域外接矩形 + cv::Rect rtValid = GetBoundingRect(matBinary); + if (rtValid == cv::Rect(0, 0, 0, 0)) + { + blobImg = cv::Mat(); + return -1; + } + + // 3. 裁剪边缘 + cv::Rect rtCrop = GetCropArea(rtValid); + cv::Mat matCrop = m_matLoad(rtCrop).clone(); + + // 4. 缩放 + 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); + + // 5. 高斯模糊 — 缓存到 m_matBlur + cv::GaussianBlur(matResized, m_matBlur, cv::Size(5, 5), 0); + + // 6. 自适应二值化检测 + m_matBlob = AdaptiveBinary(m_matBlur); + blobImg = m_matBlob.clone(); + + return 0; +} + +// ============================================================ +// TraditionalClassify — 独立分类接口 +// 对残点二值图做连通域分析+缺陷分类决策树 +// 前提: 已调用 TraditionalDetect(m_matBlur 已缓存) +// 返回: 分类到的缺陷数量,<0 表示异常 +// ============================================================ +int CTcsCheck::TraditionalClassify(const cv::Mat& blobImg) +{ + if (m_matBlur.empty() || blobImg.empty()) return -1; + + m_matBlob = blobImg.clone(); + ClassifyBlobs(m_matBlur, blobImg); + + return static_cast(m_vecDefectInfo.size()); +} + void CTcsCheck::ProcessImages(bool bDrawResult) { LoadImages();