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