feat 初步实现传统检测

dev_lsy
liusiyang 1 month ago
parent 2e6bf04df7
commit ac5c51758c

@ -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}
)

@ -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<QXImageResult> m_Draw_qxImageResult; // 缺陷小图结果
// 传统检测模块
CTcsCheck m_tcsCheck;
std::vector<cv::Rect> SmallRoiList;
std::string m_strRootPath_TA_cls;

@ -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;
}

@ -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

@ -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;

@ -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<int>(matCrop.cols * m_cpCfg.fZoomRatio));
const int outH = std::max(1, static_cast<int>(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 — 独立分类接口
// 对残点二值图做连通域分析+缺陷分类决策树
// 前提: 已调用 TraditionalDetectm_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<int>(m_vecDefectInfo.size());
}
void CTcsCheck::ProcessImages(bool bDrawResult)
{
LoadImages();

Loading…
Cancel
Save