Compare commits

...

38 Commits

Author SHA1 Message Date
liusiyang 91f99a3b7a Merge remote-tracking branch 'origin/dev_lsy' into dev_offline
4 weeks ago
liusiyang 5b8e4b7999 feat AD_YX添加HJ计算
4 weeks ago
liusiyang 870ad6fe99 Merge branch 'dev_offline' of https://gitea.aidlux.getcharzp.cn/liusiyang/BOE_FOG_DETECT into dev_offline
4 weeks ago
liusiyang 0537877995 Merge remote-tracking branch 'origin/dev_lsy' into dev_offline
4 weeks ago
liusiyang c1c6115c8a fix Detect可能越界崩溃
4 weeks ago
liusiyang 0dfbca8934 feat 添加AI_defect_Tag检测模型
1 month ago
liusiyang 78afff6caf revert 撤销使用type区分区域
1 month ago
liusiyang 768a227910 feat 初步添加BQ模型检测
1 month ago
liusiyang eb2cf825ce feat 开放标签扩/缩参数
1 month ago
liusiyang 1796a3a9ab Merge branch 'dev_lsy' into dev_offline
1 month ago
liusiyang 8610481adb fix 异物范围过滤
1 month ago
liusiyang 63385495a1 feat LD新增检测(v1.2.7)
1 month ago
liusiyang 2f5f0e1589 update 更改线的二次判断,异物添加范围
1 month ago
liusiyang 9996b5a228 feat 开放暗点同位置判断IOU(v1.2.6)
1 month ago
liusiyang 1633a1c697 fix 分类输入图片resize(v1.2.5)
1 month ago
liusiyang 49390e31bb fix 分类输入图片resize(v1.2.5)
1 month ago
liusiyang 77f0e8c54e update 使用type区分弱化和检测区域
2 months ago
liusiyang a9f6d659bd fix 删除重复定义
2 months ago
liusiyang f3ebf08fd6 Merge branch 'dev_lsy' of https://gitea.aidlux.getcharzp.cn/liusiyang/BOE_FOG_DETECT into dev_lsy
2 months ago
liusiyang 74f604ca65 update ngimg日志存储添加日期
2 months ago
liusiyang 8c5bcd7c9f feat density计算
2 months ago
liusiyang 937a5ac1d0 Merge branch 'dev_lsy' into dev_offline
2 months ago
liusiyang 4c36c7534c update 标签外扩5pix
2 months ago
liusiyang aaac47868f Revert "update 云平台改为单显卡" fog使用双显卡
2 months ago
liusiyang e47a221978 update 云平台改为单显卡
2 months ago
liusiyang c321a118d5 update 更新版本号
2 months ago
liusiyang 61731e1d38 Merge branch 'dev_lsy' into dev_offline
2 months ago
liusiyang eba708f32a fix 3s暗点漏检
2 months ago
liusiyang b535bc481e fix 异物漏检
2 months ago
liusiyang 8728bbdb68 update 标签外扩2pix
2 months ago
liusiyang 29086815bc fix 同位置3s暗点报出
2 months ago
liusiyang 036d19bbe6 Merge branch 'dev_lsy' of https://gitea.aidlux.getcharzp.cn/liusiyang/BOE_FOG_DETECT into dev_lsy
2 months ago
liusiyang 0857847444 fix 更改灰阶计算
2 months ago
liusiyang c1f4a2b451 update 同步cell参数延迟更改
2 months ago
liusiyang 0afe6c80a6 fix LD漏检
2 months ago
liusiyang 18a1394759 feat 添加RGB-HGRAY检测
2 months ago
liusiyang 44ce8cc61c offline tmp
2 months ago
liusiyang 546ce62fe6 fix AI_Time限制绘图
2 months ago

@ -174,7 +174,8 @@ public:
std::shared_ptr<AIModel_Base> AI_defect_zf; // L127 L255
std::shared_ptr<AIModel_Base> AI_defect_bq; // L127 L255
std::shared_ptr<AIModel_Base> AI_defect_bq;
std::shared_ptr<AIModel_Base> AI_defect_Tag;
std::shared_ptr<AIModel_Base> AI_defect_127Cell; // L127 L255

@ -143,7 +143,7 @@ int AIFactory::InitALLAIModle(GPU_Config gupconfig)
boe_config.inputType = AIModel_Base::Input_CHW;
AI_defect_zf->Init(boe_config);
}
// 标签 检测
// 标签 定位
if (!AI_defect_bq)
{
AI_defect_bq = AIModel_Base::GetInstance();
@ -154,6 +154,17 @@ int AIFactory::InitALLAIModle(GPU_Config gupconfig)
boe_config.inputType = AIModel_Base::Input_CHW;
AI_defect_bq->Init(boe_config);
}
// 标签 检测
if (!AI_defect_Tag)
{
AI_defect_Tag = AIModel_Base::GetInstance();
AIModel_Base::AIModelRun_Config boe_config;
boe_config.gpuconfig.copy(gupconfig);
boe_config.strPath = "/home/aidlux/BOE/UseModel_FOG/defect_L127.engine";
boe_config.strName = "tag";
boe_config.inputType = AIModel_Base::Input_CHW;
AI_defect_Tag->Init(boe_config);
}
// 字符 127cell
if (!AI_defect_127Cell)
{

@ -26,7 +26,7 @@
// resize 图片的 宽度
#define RESIZE_IMAGE_WIDTH 1680
// #define AI_Time
#define AI_Time
enum TEM_IMG_IDX_
{

@ -44,6 +44,7 @@ public:
static int SizeRect(cv::Rect &roi, int img_w, int img_h, int addw, int addh);
static int CalHj(const cv::Mat &img, const cv::Mat &mask, int b_value);
static int CalHj(const cv::Mat &img, const cv::Mat &mask, const cv::Mat &backgroundimg);
static int CalHjWeighted(const cv::Mat &img, const cv::Mat &mask, int b_value, float power = 2.0f);
static float CalRoi2RoiPre(cv::Rect rect1, cv::Rect rect2);
// 计算平均灰度
static float CalImgBrightness(cv::Mat imgRoi);

@ -153,6 +153,8 @@ public:
void AddLog(std::string str);
// 添加图片信息
int AddDetImage(std::shared_ptr<shareImage> p);
// ✅ 处理完成后释放中间数据,减少内存占用
void ReleaseIntermediateData();
DetStep getStep();
void setStep(DetStep step);
bool IsNotDet();
@ -173,6 +175,7 @@ public:
std::shared_ptr<Image_AI_Det_Result> qx_DetAIResult; // BLob 检测要用到的一些 参数 。
std::shared_ptr<Image_AI_Det_Result> cell127_DetAIResult; // 127 cell 检测要用到的一些 参数 。
std::shared_ptr<Image_AI_Det_Result> bq_DetAIResult; // BQ标签检测要用到的一些 参数 。
// 结果图片
cv::Mat resultImg;

@ -40,6 +40,7 @@ struct QX_ERROR_INFO_
float grayDis;
float fUpIou;
float density; // Blob- 密度
bool bIsStandardLD; // 是否通过LD标准判定跳过UP/DP的IOU检查
std::vector<std::string> detLogList; // 检测日志
std::shared_ptr<DetLog> detlog;
std::vector<int> detRegionidxList; // 对应检测区域的 idx.
@ -344,7 +345,10 @@ struct BQ_Result
{
int nresult = 1;
bool bShield_BQ = true; // 是否屏蔽标签
std::vector<cv::Rect> pBQ_roiList; // 标签的区域
bool bBQ_AI_Det = false; // 是否启用标签独立AI检测
std::vector<cv::Rect> pBQ_roiList; // 标签的原始区域
std::vector<cv::Rect> pBQ_expandedRoiList; // 标签的扩展后区域用于AI检测
std::vector<cv::Mat> pBQ_cropImages; // 标签扩展区域图像
std::vector<cv::Point> BQ_centerPoint; // 标签的中心点位置
};
struct CameraBaseResult

@ -73,6 +73,9 @@ public:
// 获取检测库 状态信息 返回CHECK_THREAD_RUN_STATUS
int GetStatus();
// 计算blob的密度
int CalBlobDensity_QX();
// 更新参数 pconfig 参数指针nConfigType 需要更新的参数类型 返回0 成功 其他异常
int UpdateConfig(void *pconfig, int nConfigType);
@ -191,6 +194,12 @@ private:
// 获得 127cell blob
int GetBlob_127cell();
// 获得 BQ标签 blob
int GetBlob_BQ();
// BQ标签 AI 检测
int AI_Detect_BQ();
// 对AI mask图片进行 结果处理
int AIMaskDet();

@ -2,7 +2,7 @@
#define ImgCheckBase_H_
#include <string>
#include <memory>
#define ALL_INTERFACE_VERSION 6
#define ALL_INTERFACE_VERSION 7
#define MAX_GPU_NUM 4
enum CHECK_THREAD_RUN_STATUS

@ -123,7 +123,7 @@ int AI_Edge_Algin::Detect(const cv::Mat &img, DetConfig *pDetConfig, std::shared
return 7;
}
if (max_contour_index >= 0)
if (max_contour_index >= 0&& contours.size() > 0)
{
result_roi = boundingRect(contours[max_contour_index]);
}

@ -25,6 +25,7 @@
#include <vector>
#include <mutex>
#include <algorithm>
#include <ctime>
static std::mutex g_ngImgSave_mutex;
static std::vector<std::future<void>> g_ngImgSave_futures;
@ -696,6 +697,7 @@ int CameraCheckAnalysisy::preDet_BQ(const cv::Mat &L255CutImg, std::shared_ptr<I
long ts = CheckUtil::getcurTime();
m_pCheck_Result->cameraBaseResult->pBQ_Result = std::make_shared<BQ_Result>();
m_pCheck_Result->cameraBaseResult->pBQ_Result->bShield_BQ = m_AnalysisyConfig.commonCheckConfig.baseConfig.bShield_BQ;
m_pCheck_Result->cameraBaseResult->pBQ_Result->bBQ_AI_Det = m_pbaseCheckFunction->Det_BQ.bBQ_AI_Det;
cv::Rect Det_CropRoi = m_pCheck_Result->cameraBaseResult->pEdgeDetResult->cutRoi;
@ -724,7 +726,25 @@ int CameraCheckAnalysisy::preDet_BQ(const cv::Mat &L255CutImg, std::shared_ptr<I
for (int i = 0; i < m_pCheck_Result->cameraBaseResult->pBQ_Result->pBQ_roiList.size(); i++)
{
cv::Rect boundingRect = m_pCheck_Result->cameraBaseResult->pBQ_Result->pBQ_roiList.at(i);
detImg_mask(boundingRect).setTo(255);
int BQ_expand = m_pbaseCheckFunction->Det_BQ.BQ_expand;
boundingRect.x -= BQ_expand;
boundingRect.y -= BQ_expand;
boundingRect.width += 2 * BQ_expand;
boundingRect.height += 2 * BQ_expand;
boundingRect = boundingRect & cv::Rect(0, 0, detImg_mask.cols, detImg_mask.rows);
if (boundingRect.width > 0 && boundingRect.height > 0)
{
// 同时屏蔽主AI避免重复检测
detImg_mask(boundingRect).setTo(255);
// 保存裁剪图像供独立BQ AI检测
if (m_pbaseCheckFunction->Det_BQ.bBQ_AI_Det)
{
m_pCheck_Result->cameraBaseResult->pBQ_Result->pBQ_expandedRoiList.push_back(boundingRect);
cv::Mat bqCrop = L255CutImg(boundingRect).clone();
m_pCheck_Result->cameraBaseResult->pBQ_Result->pBQ_cropImages.push_back(bqCrop);
}
}
}
if (L255->result->in_shareImage->bDebugsaveImg)
{
@ -1235,43 +1255,52 @@ int CameraCheckAnalysisy::ngImgSave() {
auto result_copy = m_pCheck_Result;
std::string save_path_root = "/home/aidlux/BOE/FOG/cloud/";
auto task = std::async(std::launch::async, [result_copy, save_path_root]() {
// 生成当天日期字符串,用于按天组织存储目录
time_t now = time(nullptr);
struct tm tm_now;
localtime_r(&now, &tm_now);
char date_buf[16];
strftime(date_buf, sizeof(date_buf), "%Y-%m-%d", &tm_now);
std::string date_str(date_buf);
auto task = std::async(std::launch::async, [result_copy, save_path_root, date_str]() {
try {
if (!result_copy) return;
// ---------- 目录清理(加锁防止并发删除)----------
// 只保留最近 2 天的数据(今天 + 昨天),删除更早的日期目录
{
static std::mutex cleanup_mutex; // 静态锁,所有实例共享
std::lock_guard<std::mutex> lock(cleanup_mutex);
// 计算 1 天前的日期作为清理截止线(保留今天和昨天共 2 天)
time_t now_cleanup = time(nullptr);
time_t cutoff_time = now_cleanup - 1 * 24 * 3600;
struct tm tm_cutoff;
localtime_r(&cutoff_time, &tm_cutoff);
char cutoff_buf[16];
strftime(cutoff_buf, sizeof(cutoff_buf), "%Y-%m-%d", &tm_cutoff);
std::string cutoff_str(cutoff_buf);
DIR* dir = opendir(save_path_root.c_str());
if (dir) {
std::vector<std::pair<std::string, time_t>> dirs;
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
std::string name = entry->d_name;
if (name == "." || name == "..") continue;
std::string full_path = save_path_root + name;
struct stat st;
if (stat(full_path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
struct stat lst;
if (lstat(full_path.c_str(), &lst) == 0 && !S_ISLNK(lst.st_mode)) {
dirs.emplace_back(full_path, st.st_mtime);
// 检查是否为日期格式目录 YYYY-MM-DD
if (name.length() == 10 && name[4] == '-' && name[7] == '-') {
// ISO 日期格式可按字典序直接比较
if (name < cutoff_str) {
std::string full_path = save_path_root + name;
struct stat st;
if (stat(full_path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
CheckUtil::DeleteDir(full_path);
}
}
}
}
closedir(dir);
if (dirs.size() > 100) {
std::sort(dirs.begin(), dirs.end(),
[](auto& a, auto& b) { return a.second < b.second; });
const std::string& oldest_dir = dirs.front().first;
if (!oldest_dir.empty() && oldest_dir != save_path_root) {
CheckUtil::DeleteDir(oldest_dir);
}
}
} else {
cerr << ("ERROR: cannot open root directory for cleanup");
}
}
@ -1300,8 +1329,8 @@ int CameraCheckAnalysisy::ngImgSave() {
!m_CheckResult_shareP->resultimg.empty() &&
!m_CheckResult_shareP->cutSrcimg.empty()) {
// 构建保存目录(使用净化后的组件
std::string save_dir = save_path_root + "/" + safe_product + "/" + safe_camera + "/";
// 构建保存目录(使用净化后的组件,包含日期子目录
std::string save_dir = save_path_root + date_str + "/" + safe_product + "/" + safe_camera + "/";
if (CheckUtil::CreateDir(save_dir) != 0) {
cerr << ("Failed to create directory: " + save_dir);
continue;
@ -1316,7 +1345,7 @@ int CameraCheckAnalysisy::ngImgSave() {
// cv::imwrite(save_dir + safe_channel + "_AIMask.png", mask_img);
// cv::imwrite(save_dir + safe_channel + "_Resultimg.png", result_img);
// cv::imwrite(save_dir + safe_channel + "_CutImg.png", cut_img);
writeLog(save_dir+ safe_channel + "log", m_CheckResult_shareP->det_LogList);
writeLog(save_dir+ safe_channel + "_log", m_CheckResult_shareP->det_LogList);
} catch (const cv::Exception& e) {
cerr << ("OpenCV exception: " + std::string(e.what()));
} catch (const std::exception& e) {

@ -457,6 +457,32 @@ int CheckUtil::CalHj(const cv::Mat &img, const cv::Mat &mask, const cv::Mat &bac
return meanValue123[0];
}
int CheckUtil::CalHjWeighted(const cv::Mat &img, const cv::Mat &mask, int b_value, float power)
{
cv::Mat imgf;
if (img.type() != CV_32F)
img.convertTo(imgf, CV_32F);
else
imgf = img;
cv::Mat absDiff = cv::abs(imgf - b_value);
cv::Mat weight;
cv::pow(absDiff, power, weight);
cv::Mat maskedDiff, maskedWeight;
absDiff.copyTo(maskedDiff, mask);
weight.copyTo(maskedWeight, mask);
cv::Scalar weightedSum = cv::sum(maskedDiff.mul(weight));
cv::Scalar weightSum = cv::sum(maskedWeight);
if (weightSum[0] < 1e-6)
return 0;
return static_cast<int>(weightedSum[0] / weightSum[0]);
}
float CheckUtil::CalRoi2RoiPre(cv::Rect rect1, cv::Rect rect2)
{
// 计算交集区域

@ -18,6 +18,49 @@ ImageAllResult::~ImageAllResult()
{
}
// ✅ 处理完成后释放中间 cv::Mat 数据,减少内存占用
// 注意CheckResultresult中的图像数据必须保留
void ImageAllResult::ReleaseIntermediateData()
{
std::lock_guard<std::mutex> lock_cam(mtx_Det);
// ✅ 释放原始输入图像(处理完成后不再需要)
if (result && result->in_shareImage)
{
if (!result->in_shareImage->img.empty())
{
result->in_shareImage->img.release();
}
if (!result->in_shareImage->AI_maskImg.empty())
{
result->in_shareImage->AI_maskImg.release();
}
}
// ✅ AI_Time 开启时需要保留 detImg用于生成缺陷小图和 AI_Qx_MaskList用于 AI 调试图像)
// 其他中间数据可以安全释放
#ifndef AI_Time
if (!detImg.empty()) { detImg.release(); }
#endif
if (!AI_detImg.empty()) { AI_detImg.release(); }
if (!AIMaskImg.empty()) { AIMaskImg.release(); }
if (!AI_127CellMaskImg.empty()) { AI_127CellMaskImg.release(); }
if (!resultImg.empty()) { resultImg.release(); }
if (!shieldImg.empty()) { shieldImg.release(); }
if (!AI_shieldImg.empty()) { AI_shieldImg.release(); }
if (!YX_MaskImg.empty()) { YX_MaskImg.release(); }
if (!LcakPol_MaskImg.empty()) { LcakPol_MaskImg.release(); }
// 释放 AI 检测中间结果
if (qx_DetAIResult) { qx_DetAIResult->Init(); }
if (cell127_DetAIResult) { cell127_DetAIResult->Init(); }
// ✅ AI_Time 开启时需要保留 AI_Qx_MaskListGetAIDetImg 使用)
#ifndef AI_Time
AI_Qx_MaskList.clear();
#endif
}
void ImageAllResult::AddLog(std::string str)
{
LogList.push_back(str);

@ -327,6 +327,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
float hj = pParam->paramArr[j].hj;
float Len = pParam->paramArr[j].length;
float md = pParam->paramArr[j].density;
float area_max = pParam->paramArr[j].area_max;
Judge_Status = true;
@ -385,6 +386,19 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
BOOL_TO_STR(flen >= Len), flen, BOOL_TO_ThanLess(flen > Len), Len);
}
if( config_qx_type == CONFIG_QX_NAME_POL_Cell && area_max > 0)
{
if(detArea > area_max)
{
nerrortype = 0;
result = true;
bNG_Status = false;
bYS_Status = false;
}
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "PolCell_Area_Max", "%s -> %f %s %f ",
BOOL_TO_STR(detArea > area_max), detArea, BOOL_TO_ThanLess(detArea > area_max), area_max);
}
if (!result)
{
break;
@ -415,6 +429,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
Judge_Status = true;
checkFlage = ANALYSIS_TYPE_TF;
nerrortype = 1;
bNG_Status = true;
}
}
// 暗点 3S 分析 对 好品 或者 是 疑是的缺陷进行3S分析。S数量分析。 3S的暗点直接NG.
@ -626,7 +641,9 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
m_CheckResult_shareP->nresult = nqx_type;
}
#ifdef AI_Time
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
#endif
m_CheckResult_shareP->qxImageResult.push_back(tem);
}
else
@ -637,7 +654,9 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
m_CheckResult_shareP->nYS_result = nqx_type;
}
#ifdef AI_Time
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
#endif
m_CheckResult_shareP->YS_ImageResult.push_back(tem);
}
@ -885,7 +904,9 @@ int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult,
{
m_CheckResult_shareP->nresult = nqx_type;
}
#ifdef AI_Time
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
#endif
m_CheckResult_shareP->qxImageResult.push_back(tem);
}
}

@ -170,7 +170,7 @@ int ImgCheckAnalysisy::GetStatus()
std::string ImgCheckAnalysisy::GetVersion()
{
return std::string("BOE_1.2.0");
return std::string("BOE_1.2.8_" + std::string(__DATE__) + "_" + std::string(__TIME__));
}
std::string ImgCheckAnalysisy::GetErrorInfo()
@ -332,7 +332,7 @@ cv::Scalar ImgCheckAnalysisy::calc_blob_info_withstats(cv::Mat &img, const cv::M
// 计算 hj差异图像大于0的像素均值
// double hj = std::abs(fbk - fdet);
double hj = CheckUtil::CalHj(cimg, cmask, mean_bk.val[0]);
double hj = CheckUtil::CalHjWeighted(cimg, cmask, fbk, 2.0f);
int worb = 0;
if (fdet >= fbk)
@ -381,7 +381,7 @@ double ImgCheckAnalysisy::CalBlobHJ(cv::Mat &img, const cv::Mat &mask, cv::Rect
// 计算 hj差异图像大于0的像素均值
// double hj = std::abs(fbk - fdet);
double hj = CheckUtil::CalHj(cimg, cmask, fbk);
double hj = CheckUtil::CalHjWeighted(cimg, cmask, fbk, 2.0f);
static int kkk = 0;
unsigned char *pErrordata = (unsigned char *)cimg.data;
@ -518,6 +518,14 @@ int ImgCheckAnalysisy::CheckRun()
Edge_Det();
// BQ标签AI检测
{
long t_BQ_s = CheckUtil::getcurTime();
int recBQ = AI_Detect_BQ();
long t_BQ_e = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_0, "4、BQ Detect", "-------------------------BQ AI Detect--------%ld ms-------\n", t_BQ_e - t_BQ_s);
}
m_CheckResult_shareP->resultMaskImg = m_pImageAllResult->qx_DetAIResult->AI_MaskImg;
// cv::imwrite("dddddddd.png", m_pImageAllResult->qx_DetAIResult->AI_MaskImg);
@ -589,6 +597,7 @@ int ImgCheckAnalysisy::SetNewConfig()
m_pRegionAnalysisyParam = &m_pCommonAnalysisyConfig->regionConfigArr.at(0);
GetParamidx();
UpdateImgageScale();
UPdateLDConfig();
if (true)
{
printf("SetNewConfig m_nConfigIdx %d m_CheckConfig.strSkuName %s \n", m_nConfigIdx, m_AnalysisyConfig.strSkuName.c_str());
@ -666,6 +675,8 @@ int ImgCheckAnalysisy::Run(int nId)
CheckRun();
m_nRun_Status = CHECK_THREAD_STATUS_COMPLETE;
m_pImageAllResult->setStep(ImageAllResult::DetStep_Complet);
// ✅ 处理完成后立即释放 ImageAllResult 的中间数据,减少内存占用
m_pImageAllResult->ReleaseIntermediateData();
m_nRun_Status = CHECK_THREAD_STATUS_IDLE;
// printf("*--------%d\n", m_nRun_Status);
}
@ -1448,6 +1459,9 @@ int ImgCheckAnalysisy::GetCheckResultBLob()
BLobToDetResult();
// 计算缺陷密度
CalBlobDensity_QX();
return 0;
}
@ -1691,6 +1705,8 @@ int ImgCheckAnalysisy::GetALLBlob()
re = GetBlob_QX();
long t4 = CheckUtil::getcurTime();
re = GetBlob_127cell();
long t5 = CheckUtil::getcurTime();
re = GetBlob_BQ();
if (blobs.blobCount > 100)
{
@ -1700,8 +1716,8 @@ int ImgCheckAnalysisy::GetALLBlob()
long te = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_0, strBaseLog,
"---GetALLBlob End ;use time %ld ms yx %ld ms lack %ld ms qx %ld ms 127 %ld ms",
te - t1, t2 - t1, t3 - t2, t4 - t3, te - t4);
"---GetALLBlob End ;use time %ld ms yx %ld ms lack %ld ms qx %ld ms 127 %ld ms bq %ld ms",
te - t1, t2 - t1, t3 - t2, t4 - t3, t5 - t4, te - t5);
// getchar();
return 0;
@ -1757,6 +1773,10 @@ int ImgCheckAnalysisy::GetBLob_YX()
cv::findContours(erodedImage, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
int nresult = 0;
// 将 mask 一次性 resize 到 detImg 尺寸,避免循环内反复 resize
cv::Mat maskImg;
cv::resize(erodedImage, maskImg, cv::Size(m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows), 0, 0, cv::INTER_AREA);
for (size_t i = 0; i < contours.size(); ++i)
{
@ -1774,7 +1794,15 @@ int ImgCheckAnalysisy::GetBLob_YX()
roi.y *= fy_src;
roi.height *= fy_src;
float len = std::sqrt(roi.width * roi.width + roi.height * roi.height);
// 精确计算长度
float re_len = Cal_QXLen(maskImg(roi), config_qx_type, fx_src, fy_src);
cv::Scalar result = calc_blob_info_withstats(m_pImageAllResult->detImg, maskImg, roi);
double min_val, max_val;
cv::Point min_loc, max_loc;
cv::minMaxLoc(m_pImageAllResult->detImg(roi), &min_val, &max_val, &min_loc, &max_loc);
if (true)
{
@ -1784,12 +1812,12 @@ int ImgCheckAnalysisy::GetBLob_YX()
temerror.area = area;
temerror.JudgArea = judgeArea;
temerror.JudgArea_second = judgeArea;
temerror.energy = area * fx_src * fy_src;
temerror.flen = len;
temerror.energy = result[1];
temerror.flen = re_len;
temerror.nconfig_qx_type = config_qx_type;
temerror.qx_name = qx_name;
temerror.maxValue = 0;
temerror.grayDis = 0;
temerror.maxValue = max_val;
temerror.grayDis = result[2];
temerror.density = 0;
temerror.fUpIou = 0;
@ -2139,6 +2167,199 @@ int ImgCheckAnalysisy::GetBlob_127cell()
return 0;
}
// BQAI检测
int ImgCheckAnalysisy::AI_Detect_BQ()
{
std::string strBaseLog = "AI_Detect_BQ";
// 检查BQ标签AI检测是否启用
if (!m_pImageAllResult->cameraBaseResult->pBQ_Result)
{
return 0;
}
if (!m_pImageAllResult->cameraBaseResult->pBQ_Result->bBQ_AI_Det)
{
return 0;
}
int bqNum = static_cast<int>(m_pImageAllResult->cameraBaseResult->pBQ_Result->pBQ_cropImages.size());
if (bqNum <= 0)
{
return 0;
}
m_pdetlog->AddCheckstr(PrintLevel_1, strBaseLog, "============= Start, BQ num = %d", bqNum);
long ts = CheckUtil::getcurTime();
// 初始化BQ AI检测结果
m_pImageAllResult->bq_DetAIResult = std::make_shared<ImageAllResult::Image_AI_Det_Result>();
std::shared_ptr<ImageAllResult::Image_AI_Det_Result> pDetAIResult = m_pImageAllResult->bq_DetAIResult;
// 使用与主检测相同的AI模型后续更改为BQ AI模型
std::shared_ptr<AIModel_Base> pAI_Model = AI_Factory->AI_defect_Tag;
int AIInputImg_width = pAI_Model->input_0.width;
int AIInputImg_height = pAI_Model->input_0.height;
cv::Size modelInputSize(AIInputImg_width, AIInputImg_height);
// 创建全图大小的AI mask与主检测detImg同尺寸
cv::Mat AI_detImage = m_pImageAllResult->AI_detImg;
pDetAIResult->AI_MaskImg = cv::Mat::zeros(AI_detImage.size(), CV_8UC1);
// 先提交所有BQ AI推理任务仿照AI_Detect_QX的提交-收集模式)
int submitted = 0;
int completed = 0;
while (completed < bqNum)
{
// 提交任务限制并发数不超过2避免占用过多资源
if (submitted < bqNum && runner->GetProcessingCount() < 2)
{
cv::Mat bqCrop = m_pImageAllResult->cameraBaseResult->pBQ_Result->pBQ_cropImages.at(submitted);
if (!bqCrop.empty())
{
cv::Mat resizedCrop;
cv::resize(bqCrop, resizedCrop, modelInputSize);
std::shared_ptr<AIMulThreadRunBase::AITask> task = std::make_shared<AIMulThreadRunBase::AITask>();
task->id = submitted;
task->roi = cv::Rect(0, 0, modelInputSize.width, modelInputSize.height);
task->input = resizedCrop;
task->output = std::make_shared<cv::Mat>();
task->engine = pAI_Model;
runner->SubmitTask(task);
}
submitted++;
}
// 收集已完成的结果
std::shared_ptr<AIMulThreadRunBase::AITask> result;
if (runner->PopResult(result))
{
int idx = result->id;
if (idx >= 0 && idx < bqNum)
{
cv::Rect bqRoi = m_pImageAllResult->cameraBaseResult->pBQ_Result->pBQ_expandedRoiList.at(idx);
cv::Rect validRoi = bqRoi & cv::Rect(0, 0, AI_detImage.cols, AI_detImage.rows);
if (validRoi.width > 0 && validRoi.height > 0)
{
cv::Mat &outMask = *(result->output);
if (!outMask.empty())
{
// 将AI输出mask resize回BQ裁剪图原始尺寸
cv::Mat resizedMask;
cv::resize(outMask, resizedMask, cv::Size(bqRoi.width, bqRoi.height));
// 将BQ的mask结果放到全图mask的对应位置
resizedMask.copyTo(pDetAIResult->AI_MaskImg(validRoi), resizedMask);
m_pdetlog->AddCheckstr(PrintLevel_2, strBaseLog, "BQ[%d] AI done, roi=[%d,%d,%d,%d]",
idx, validRoi.x, validRoi.y, validRoi.width, validRoi.height);
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_2, strBaseLog, "BQ[%d] AI output empty", idx);
}
}
}
completed++;
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
long te = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_1, strBaseLog, "============= End, time=%ld ms", te - ts);
// 调试存图
if (DetImgInfo_shareP->bDebugsaveImg)
{
cv::imwrite(m_strCurDetCamChannel + "_AI_BQ_mask.png", pDetAIResult->AI_MaskImg);
}
return 0;
}
int ImgCheckAnalysisy::GetBlob_BQ()
{
std::string strBaseLog = "GetBlob_BQ";
// 检查BQ标签AI检测是否启用
if (!m_pImageAllResult->cameraBaseResult->pBQ_Result)
{
return 0;
}
if (!m_pImageAllResult->cameraBaseResult->pBQ_Result->bBQ_AI_Det)
{
return 0;
}
if (!m_pImageAllResult->bq_DetAIResult)
{
m_pdetlog->AddCheckstr(PrintLevel_2, strBaseLog, "bq_DetAIResult is null, skip");
return 0;
}
std::shared_ptr<ImageAllResult::Image_AI_Det_Result> pDetAIResult = m_pImageAllResult->bq_DetAIResult;
cv::Mat maskimg = pDetAIResult->AI_MaskImg;
if (maskimg.empty())
{
m_pdetlog->AddCheckstr(PrintLevel_2, strBaseLog, "AI_MaskImg is empty, skip");
return 0;
}
long t1 = CheckUtil::getcurTime();
// 使用与主流程相同的blob提取方式GetBlobs_V3
// BQ检测的mask中缺陷像素值为非零值
ERROR_DOTS_BLOBS blobs_bq;
memset(&blobs_bq, 0x00, sizeof(ERROR_DOTS_BLOBS));
unsigned char *pGrayErrordata = (unsigned char *)maskimg.data;
int width = maskimg.cols;
int height = maskimg.rows;
int minArea = 10; // BQ标签区域最小缺陷面积阈值
// 使用逐像素扫描方式提取blobGetBlobs_V3对mask中的非零值进行blob提取
GetBlobs_V3(&blobs_bq, pGrayErrordata, width, height, minArea);
// 设置BQ blob的默认缺陷类型后续分类会重新确定类型
// 使用非cell类型确保进入AI_Classify_New分类流程
for (int i = 0; i < blobs_bq.blobCount; i++)
{
blobs_bq.blobTab[i].ErrType = CONFIG_QX_NAME_LD; // 以亮点作为初始类型,后续分类修正
}
// 将BQ的blob汇入主blob列表
PushBlob(&blobs, &blobs_bq);
long te = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_1, strBaseLog,
"BQ blob num = %d, merged to main blobs (total=%d), time=%ld ms",
blobs_bq.blobCount, blobs.blobCount, te - t1);
// 调试存图
if (DetImgInfo_shareP->bDebugsaveImg && blobs_bq.blobCount > 0)
{
cv::Mat tm;
cv::cvtColor(maskimg, tm, cv::COLOR_GRAY2RGB);
for (int i = 0; i < blobs_bq.blobCount; i++)
{
cv::Rect roi;
roi.x = blobs_bq.blobTab[i].minx;
roi.y = blobs_bq.blobTab[i].miny;
roi.width = blobs_bq.blobTab[i].maxx - blobs_bq.blobTab[i].minx + 1;
roi.height = blobs_bq.blobTab[i].maxy - blobs_bq.blobTab[i].miny + 1;
cv::rectangle(tm, roi, cv::Scalar(0, 0, 255), 2);
}
cv::imwrite(m_strCurDetCamChannel + "_BQ_blob.png", tm);
}
return 0;
}
int ImgCheckAnalysisy::AIMaskDet()
{
m_pdetlog->AddCheckstr(PrintLevel_0, "AIMaskDet", "=======start");
@ -2482,6 +2703,11 @@ int ImgCheckAnalysisy::AI_Detect_QX()
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, strBaseLog, "=======model use Chess");
pAI_Model = AI_Factory->AI_defect_Chess;
}
else if (m_pFuntion->function.f_BaseDet.strAIMode == "RGB-HGRAY")
{
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, strBaseLog, "=======model use RE_RGBHGRAY");
pAI_Model = AI_Factory->AI_defect_RE_RGBHGRAY;
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, strBaseLog, "=======model use base");
@ -3351,23 +3577,40 @@ int ImgCheckAnalysisy::BLobToDetResult()
}
}
bool bLD_Standard = false; // 是否通过LD标准判定跳过UP/DP的IOU检查
if (config_qx_type == CONFIG_QX_NAME_MTX ||
config_qx_type == CONFIG_QX_NAME_POL_Cell ||
config_qx_type == CONFIG_QX_NAME_Other ||
config_qx_type == CONFIG_QX_NAME_LD)
{
std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type];
qx_name = CONFIG_QX_NAME_Names[config_qx_type];
// m_TemCheck.AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "LD", "stsrt config_qx_type %s ", qx_name.c_str());
int dbresult = LDJudge(config_qx_type, roi, JudgArea, blobs.blobTab[i].maxValue, blobs.blobTab[i].grayDis, pQxlog);
int detre = 1;
int dbresult = -1;
if(m_pFuntion->function.f_LDConfig.bOpen && m_pFuntion->function.f_LDConfig.bUseLD_Standard)
{
if(blobs.blobTab[i].JudgArea >= m_pFuntion->function.f_LDConfig.fLD_Area &&
blobs.blobTab[i].grayDis >= m_pFuntion->function.f_LDConfig.fLD_HJ &&
blobs.blobTab[i].energy >= m_pFuntion->function.f_LDConfig.fLD_En &&
blobs.blobTab[i].len >= m_pFuntion->function.f_LDConfig.fLD_Len)
{
dbresult = 1;
bLD_Standard = true;
}
pQxlog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "LD Analysis", "Area %0.2f > %0.2f , hj %0.2f > %0.2f , Energy %0.2f > %0.2f , len %0.2f > %0.2f",
blobs.blobTab[i].JudgArea, m_pFuntion->function.f_LDConfig.fLD_Area, blobs.blobTab[i].grayDis, m_pFuntion->function.f_LDConfig.fLD_HJ, blobs.blobTab[i].energy, m_pFuntion->function.f_LDConfig.fLD_En, blobs.blobTab[i].len, m_pFuntion->function.f_LDConfig.fLD_Len);
}
if(dbresult != 1)
{
dbresult = LDJudge(config_qx_type, roi, JudgArea, blobs.blobTab[i].maxValue, blobs.blobTab[i].grayDis, pQxlog);
}
//int detre = 1;
// 表示L0 和 DP 都有的 亮的
if (dbresult == 1)
{
config_qx_type = CONFIG_QX_NAME_LD;
qx_name = CONFIG_QX_NAME_Names[config_qx_type];
}
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, " LD Judge ", "%s qx %s ", Re_TO_STR_False(detre), qx_name.c_str());
}
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, " LD Judge ", "%s qx %s ", Re_TO_STR_Pass_1(dbresult), qx_name.c_str()); }
// 如果是chess 画面缺陷类型直接是chess异常。
if (m_pFuntion->function.f_AIQX.bAllToChess)
{
@ -3428,8 +3671,8 @@ int ImgCheckAnalysisy::BLobToDetResult()
QX_Stauts qx_status = QX_Stauts_Analysis;
// 和UP画面进行IOU判断
if (m_pFuntion->function.f_UseUpQX.bOpen)
// 和UP画面进行IOU判断 (通过亮点标准的缺陷跳过此检查)
if (!bLD_Standard && m_pFuntion->function.f_UseUpQX.bOpen)
{
if (fupS >= m_pFuntion->function.f_UseUpQX.fIOU)
{
@ -3441,6 +3684,10 @@ int ImgCheckAnalysisy::BLobToDetResult()
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "Up Mask judge", "UseUpQX.bOpen = true ;fupS = %0.2f < %02f ", fupS, m_pFuntion->function.f_UseUpQX.fIOU);
}
}
else if (bLD_Standard)
{
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "Up Mask judge", "bLD_Standard = true, skip UP/DP IOU check ;fupS = %0.2f ", fupS);
}
else
{
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "Up Mask judge", "UseUpQX.bOpen = false ;fupS = %0.2f ", fupS);
@ -3450,7 +3697,7 @@ int ImgCheckAnalysisy::BLobToDetResult()
bool ban = JudgeQXAnalysis(config_qx_type, pQxlog);
if (!ban)
{
std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type];
qx_name = CONFIG_QX_NAME_Names[config_qx_type];
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "Judge QX", "qx function close, config_qx_type %s Not Det",
qx_name.c_str());
@ -3467,7 +3714,7 @@ int ImgCheckAnalysisy::BLobToDetResult()
isMarksheildQX = Judge_MarkLine_QX(config_qx_type, roi, pQxlog);
if (isMarksheildQX)
{
std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type];
qx_name = CONFIG_QX_NAME_Names[config_qx_type];
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "MarkLine_QX", "MarkLine_QX close, config_qx_type %s Not Det",
qx_name.c_str());
@ -3496,6 +3743,7 @@ int ImgCheckAnalysisy::BLobToDetResult()
temerror.grayDis = blobs.blobTab[i].grayDis;
temerror.density = blobs.blobTab[i].density;
temerror.fUpIou = fupS;
temerror.bIsStandardLD = bLD_Standard;
temerror.whiteOrBlack = blobs.blobTab[i].whiteOrblack;
temerror.qx_status = qx_status;
@ -3738,11 +3986,13 @@ int ImgCheckAnalysisy::AI_Classify_New(const cv::Mat &src_Img, cv::Rect qx_roi,
if (3 != AI_detImage.channels())
{
cv::cvtColor(AI_detImage(task->roi), task->input, cv::COLOR_GRAY2BGR);
cv::Mat temsizeimg;
cv::resize(AI_detImage(task->roi), temsizeimg, sz);
cv::cvtColor(temsizeimg, task->input, cv::COLOR_GRAY2BGR);
}
else
{
task->input = AI_detImage(task->roi).clone();
cv::resize(AI_detImage(task->roi), task->input, sz);
}
task->engine = pAIDet;
@ -3756,6 +4006,12 @@ int ImgCheckAnalysisy::AI_Classify_New(const cv::Mat &src_Img, cv::Rect qx_roi,
if (runner->PopResult(result))
{
// if (true)
// {
// printf("result->cls_score %f\n",result->cls_score);
// std::string classpath = std::to_string(result->cls_label) + "_" + std::to_string(result->cls_score) + ".png";
// cv::imwrite(classpath, result->input);
// }
int cls_num = result->cls_label;
if (cls_num >= 0 && cls_num < maxcls)
{
@ -3884,6 +4140,7 @@ int ImgCheckAnalysisy::AI_Classify_New(const cv::Mat &src_Img, cv::Rect qx_roi,
if (true)
{
// 长宽比
bool bIsLine = false;
float flenr = 1;
int len = 0;
int widt = 0;
@ -3900,29 +4157,42 @@ int ImgCheckAnalysisy::AI_Classify_New(const cv::Mat &src_Img, cv::Rect qx_roi,
len = qx_roi.height;
}
// 长宽比过大,判断成
if (flenr > 18 &&
len > 70 &&
widt < 220 &&
fjustarea < 800)
{
// printf("\n\n\n\n\n\n\n\n\n--------- flenr %f len %d,widt %d,fjustarea %f\n", flenr,len,widt,fjustarea);
// getchar();
cls_num = AI_CLass_QX_NAME_line;
strclassName = "line";
}
// 长宽比过小,判断成
else if (flenr > 4 &&
len > 180 &&
widt < 220 &&
fjustarea < 800 &&
fjustarea > 20)
// // 长宽比过大,判断成
// if (flenr > 18 &&
// len > 70 &&
// widt < 220 &&
// fjustarea < 800)
// {
// // printf("\n\n\n\n\n\n\n\n\n--------- flenr %f len %d,widt %d,fjustarea %f\n", flenr,len,widt,fjustarea);
// // getchar();
// cls_num = AI_CLass_QX_NAME_line;
// strclassName = "line";
// bIsLine = true;
// }
// // 长宽比过小,判断成
// else if (flenr > 4 &&
// len > 180 &&
// widt < 220 &&
// fjustarea < 800 &&
// fjustarea > 20)
// {
// // printf("\n\n\n\n\n\n\n\n\n--------- flenr %f len %d,widt %d,fjustarea %f\n", flenr,len,widt,fjustarea);
// // getchar();
// cls_num = AI_CLass_QX_NAME_line;
// strclassName = "line";
// bIsLine = true;
// }
//长宽比满足,判断为线
if (flenr > 10)
{
// printf("\n\n\n\n\n\n\n\n\n--------- flenr %f len %d,widt %d,fjustarea %f\n", flenr,len,widt,fjustarea);
// getchar();
cls_num = AI_CLass_QX_NAME_line;
strclassName = "line";
bIsLine = true;
}
m_pdetlog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "AI_Classify_line", "bIsLine %d, flenr %f len %d,widt %d,fjustarea %f", bIsLine, flenr,len,widt,fjustarea);
}
}
@ -4433,3 +4703,104 @@ bool ImgCheckAnalysisy::JudgeQXAnalysis(int nqx_configType, std::shared_ptr<DetL
return re;
}
int ImgCheckAnalysisy::CalBlobDensity_QX()
{
float fs_x = m_fImgage_Scale_X;
float fs_y = m_fImgage_Scale_Y;
double dis_T = m_pBasicConfig->density_R_mm;
if (dis_T <= 0 || dis_T > 99999)
{
dis_T = 5;
}
if (!m_pDetResult || !m_pDetResult->pQx_ErrorList || m_pDetResult->pQx_ErrorList->size() <= 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, "Density_QX", "pQx_ErrorList is empty, skip density calc");
return 0;
}
m_pdetlog->AddCheckstr(PrintLevel_1, "Density_QX", "Start: dis_T=%.1fmm qx_count=%zu", dis_T, m_pDetResult->pQx_ErrorList->size());
for (int i = 0; i < m_pDetResult->pQx_ErrorList->size(); i++)
{
m_pDetResult->pQx_ErrorList->at(i).density = 1;
if (m_pDetResult->pQx_ErrorList->at(i).nconfig_qx_type == CONFIG_QX_NAME_MTX ||
m_pDetResult->pQx_ErrorList->at(i).nconfig_qx_type == CONFIG_QX_NAME_POL_Cell ||
m_pDetResult->pQx_ErrorList->at(i).nconfig_qx_type == CONFIG_QX_NAME_LD ||
m_pDetResult->pQx_ErrorList->at(i).nconfig_qx_type == CONFIG_QX_NAME_AD)
{
/* code */
}
else
{
continue;
}
cv::Rect roi = m_pDetResult->pQx_ErrorList->at(i).roi;
cv::Point p;
p.x = roi.x + roi.width * 0.5;
p.y = roi.y + roi.height * 0.5;
int num = 1;
double sum_dis = 0;
for (int j = 0; j < m_pDetResult->pQx_ErrorList->size(); j++)
{
if (i == j)
{
continue;
}
if (m_pDetResult->pQx_ErrorList->at(j).nconfig_qx_type == CONFIG_QX_NAME_MTX ||
m_pDetResult->pQx_ErrorList->at(j).nconfig_qx_type == CONFIG_QX_NAME_POL_Cell ||
m_pDetResult->pQx_ErrorList->at(j).nconfig_qx_type == CONFIG_QX_NAME_LD ||
m_pDetResult->pQx_ErrorList->at(j).nconfig_qx_type == CONFIG_QX_NAME_AD)
{
/* code */
}
else
{
continue;
}
cv::Rect roi123 = m_pDetResult->pQx_ErrorList->at(j).roi;
cv::Point p123;
p123.x = roi123.x + roi123.width * 0.5;
p123.y = roi123.y + roi123.height * 0.5;
double dis_x = std::abs(p123.x - p.x) * fs_x;
double dis_y = std::abs(p123.y - p.y) * fs_y;
double dis = std::sqrt(dis_x * dis_x + dis_y * dis_y);
if (dis > dis_T)
{
continue;
}
num++;
sum_dis += dis;
}
float avdis = dis_T;
if (num > 1)
{
avdis = sum_dis / (num - 1);
}
float fScore = (dis_T - avdis) / dis_T;
double fD = num + fScore;
m_pDetResult->pQx_ErrorList->at(i).density = fD;
m_pdetlog->AddCheckstr(PrintLevel_2, "Density_QX", " idx=%d type=%d(%s) roi[%d,%d,%d,%d] near_num=%d avgDis=%.2f score=%.3f density=%.2f",
i,
m_pDetResult->pQx_ErrorList->at(i).nconfig_qx_type,
m_pDetResult->pQx_ErrorList->at(i).qx_name.c_str(),
roi.x, roi.y, roi.width, roi.height,
num, avdis, fScore, fD);
}
m_pdetlog->AddCheckstr(PrintLevel_1, "Density_QX", "End: processed %zu defects", m_pDetResult->pQx_ErrorList->size());
return 0;
}

@ -485,7 +485,7 @@ int QX_Merge_Analysis::Analysis_single_Config(QXAnalysis_Config *pconfig, QX_cha
fmaxLen = pqxList->qxList.at(i).length;
}
// 长度超过阈值
if (pqxList->qxList.at(i).length > pconfig->len)
if (pconfig->len > 0 && pqxList->qxList.at(i).length > pconfig->len)
{
blenerror = true;
pqxList->qxList.at(i).nqx_type = QX_ERROR_TYPE_Len;
@ -593,7 +593,7 @@ int QX_Merge_Analysis::Analysis_single_Config(QXAnalysis_Config *pconfig, QX_cha
{
int minidx = -1;
bool bdis = true;
bool bdis = (pconfig->dis > 0);
double remindis = 0;
// 对距离有要求
if (pconfig->dis > 0)
@ -612,6 +612,11 @@ int QX_Merge_Analysis::Analysis_single_Config(QXAnalysis_Config *pconfig, QX_cha
allmindis = pqxList->qxList.at(i).tem_dis;
}
}
else
{
// 无邻近缺陷,距离检查自动通过
bdis = false;
}
remindis = pqxList->qxList.at(i).tem_dis;
}
@ -847,11 +852,20 @@ int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx)
int list_idx = -1;
int channel_s = 0;
int roi_expand = (round)(pAd_checkParma->analysis_iou.AD_Expand);
bool bnew_add = true;
for (int idx = 0; idx < AD_list.size(); idx++)
{
cv::Rect tem_roi(AD_list.at(idx).roi.x-roi_expand, AD_list.at(idx).roi.y-roi_expand, AD_list.at(idx).roi.width+2*roi_expand, AD_list.at(idx).roi.height+2*roi_expand);
cv::Rect tem_roi2(roi.x-roi_expand, roi.y-roi_expand, roi.width+2*roi_expand, roi.height+2*roi_expand);
float fiou = CheckUtil::CalIoU(AD_list.at(idx).roi, roi);
if (fiou > 0.15)
m_pMergedetlog->AddCheckstr(PrintLevel_3, 3, "CalIoU",
"roi1 %s roi2 %s expand %d iou %f",
CheckUtil::GetRectString(AD_list.at(idx).roi).c_str(),
CheckUtil::GetRectString(roi).c_str(),
roi_expand, fiou);
if (fiou > pAd_checkParma->analysis_iou.AD_IOU)
{
list_idx = idx;
}
@ -972,6 +986,7 @@ int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx)
}
A_2S_num += AD_list.at(ad_i).num_2s;
A_3S_num += AD_list.at(ad_i).num_3s;
if(AD_list.at(ad_i).num_1s >= 3) A_3S_num++;
m_pMergedetlog->AddCheckstr(PrintLevel_2, 3, "AD RGBL255", "AD_list %d %s",
ad_i, AD_list.at(ad_i).GetInfo().c_str());
}
@ -1028,7 +1043,7 @@ int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx)
}
else
{
m_pMergedetlog->AddCheckstr(PrintLevel_2, 3, "AD RGBL255", "3S Analysis --> %s result NG 3S %d < 1",
m_pMergedetlog->AddCheckstr(PrintLevel_2, 3, "AD RGBL255", "3S Analysis --> %s result OK 3S %d < 1",
pQXChannelList->channel_name.c_str(), A_3S_num);
}
}

@ -869,6 +869,12 @@ struct Function_AI_LD
bool bWTBLD; // 是否是WTB亮点
bool bHSLD; // 黑闪类型的亮点
float fDP_IOU; // DP的IOU
bool bUseLD_Standard; // 是否使用亮点标准化参数
float fLD_Area; // 亮点面积
float fLD_En; // 亮点的能量
float fLD_HJ; // 亮点灰阶
float fLD_Len; // 亮点长度
Function_AI_LD()
{
Init();
@ -880,6 +886,11 @@ struct Function_AI_LD
bWTBLD = false;
bHSLD = false;
fDP_IOU = 0.1;
fLD_Area = -1;
fLD_En = -1;
fLD_HJ = -1;
fLD_Len = -1;
bUseLD_Standard = false;
}
void copy(Function_AI_LD tem)
{
@ -888,6 +899,11 @@ struct Function_AI_LD
this->bWTBLD = tem.bWTBLD;
this->bHSLD = tem.bHSLD;
this->fDP_IOU = tem.fDP_IOU;
this->fLD_Area = tem.fLD_Area;
this->fLD_En = tem.fLD_En;
this->fLD_HJ = tem.fLD_HJ;
this->fLD_Len = tem.fLD_Len;
this->bUseLD_Standard = tem.bUseLD_Standard;
}
void print(std::string str)
{
@ -1519,6 +1535,34 @@ struct AD_Analysisy_S
return str123;
}
};
// 暗点同位置判断(IOU)
struct AD_Analysisy_IOU
{
float AD_IOU; // IOU 阈值
float AD_Expand; // 扩张阈值
AD_Analysisy_IOU()
{
Init();
}
void Init()
{
AD_IOU = 0.15f;
AD_Expand = 0.0f;
}
void copy(AD_Analysisy_IOU tem)
{
this->AD_IOU = tem.AD_IOU;
this->AD_Expand = tem.AD_Expand;
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%s:AD_IOU %f AD_Expand %f;", str.c_str(),
AD_IOU, AD_Expand);
std::string str123 = buffer;
return str123;
}
};
// 暗点检测功能
struct Function_AD_Check
{
@ -1530,6 +1574,7 @@ struct Function_AD_Check
AD_Analysisy_Num analysis_num; // 数量分析
AD_Analysisy_Dis analysis_dis; // 距离分析
AD_Analysisy_S analysis_s; // s 标准分析
AD_Analysisy_IOU analysis_iou; // IOU 分析
Function_AD_Check()
{
@ -1544,6 +1589,7 @@ struct Function_AD_Check
analysis_num.Init();
analysis_dis.Init();
analysis_s.Init();
analysis_iou.Init();
}
void copy(Function_AD_Check tem)
@ -1555,16 +1601,18 @@ struct Function_AD_Check
this->analysis_num.copy(tem.analysis_num);
this->analysis_dis.copy(tem.analysis_dis);
this->analysis_s.copy(tem.analysis_s);
this->analysis_iou.copy(tem.analysis_iou);
}
void print(std::string str)
{
printf("%s>>bOpen %d %s %s %s %s %s %s\n", str.c_str(),
printf("%s>>bOpen %d %s %s %s %s %s %s %s\n", str.c_str(),
bOpen, S_standard_3s.GetInfo("3S").c_str(),
S_standard_2s.GetInfo("2S").c_str(),
S_standard_1s.GetInfo("1S").c_str(),
analysis_num.GetInfo("analysis_num").c_str(),
analysis_dis.GetInfo("analysis_dis").c_str(),
analysis_s.GetInfo("analysis_s").c_str());
analysis_s.GetInfo("analysis_s").c_str(),
analysis_iou.GetInfo("analysis_iou").c_str());
}
std::string GetInfo(std::string str)
{
@ -1577,6 +1625,7 @@ struct Function_AD_Check
str123 += analysis_num.GetInfo("analysis_num");
str123 += analysis_dis.GetInfo("analysis_dis");
str123 += analysis_s.GetInfo("analysis_s");
str123 += analysis_iou.GetInfo("analysis_iou");
return str123;
}
@ -2155,6 +2204,7 @@ struct Base_Function_AD_Check
AD_Analysisy_Num analysis_num; // 数量分析
AD_Analysisy_Dis analysis_dis; // 距离分析
AD_Analysisy_S analysis_s; // s 标准分析
AD_Analysisy_IOU analysis_iou; // IOU 分析
Base_Function_AD_Check()
{
@ -2169,6 +2219,7 @@ struct Base_Function_AD_Check
analysis_num.Init();
analysis_dis.Init();
analysis_s.Init();
analysis_iou.Init();
}
void copy(Base_Function_AD_Check tem)
@ -2180,6 +2231,7 @@ struct Base_Function_AD_Check
this->analysis_num.copy(tem.analysis_num);
this->analysis_dis.copy(tem.analysis_dis);
this->analysis_s.copy(tem.analysis_s);
this->analysis_iou.copy(tem.analysis_iou);
}
void print(std::string str)
{
@ -2189,7 +2241,8 @@ struct Base_Function_AD_Check
S_standard_1s.GetInfo("1S").c_str(),
analysis_num.GetInfo("analysis_num").c_str(),
analysis_dis.GetInfo("analysis_dis").c_str(),
analysis_s.GetInfo("analysis_s").c_str());
analysis_s.GetInfo("analysis_s").c_str(),
analysis_iou.GetInfo("analysis_iou").c_str());
}
std::string GetInfo(std::string str)
{
@ -2202,11 +2255,46 @@ struct Base_Function_AD_Check
str123 += analysis_num.GetInfo("analysis_num");
str123 += analysis_dis.GetInfo("analysis_dis");
str123 += analysis_s.GetInfo("analysis_s");
str123 += analysis_iou.GetInfo("analysis_iou");
return str123;
}
};
struct Base_Function_Det_BQ
{
int BQ_expand;
bool bBQ_AI_Det; // 是否启用标签独立AI检测汇入主流程分类
Base_Function_Det_BQ()
{
Init();
}
void Init()
{
BQ_expand = 0;
bBQ_AI_Det = false;
}
void copy(Base_Function_Det_BQ tem)
{
this->BQ_expand = tem.BQ_expand;
this->bBQ_AI_Det = tem.bBQ_AI_Det;
}
void print(std::string str)
{
printf("%d BQ_expand %d bBQ_AI_Det %d\n", str.c_str(),
BQ_expand, bBQ_AI_Det);
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%d BQ_expand %d bBQ_AI_Det %d\n", str.c_str(),
BQ_expand, bBQ_AI_Det);
std::string str123 = buffer;
return str123;
}
};
// 基础检测功能
struct BaseCheckFunction
{
@ -2216,6 +2304,7 @@ struct BaseCheckFunction
Base_Function_BigNG bigNG;
Base_Function_EdgeChannel edgeChannel;
Base_Function_AD_Check ad_check;
Base_Function_Det_BQ Det_BQ;
BaseCheckFunction()
{
Init();
@ -2228,6 +2317,7 @@ struct BaseCheckFunction
bigNG.Init();
edgeChannel.Init();
ad_check.Init();
Det_BQ.Init();
}
void copy(BaseCheckFunction tem)
{
@ -2237,6 +2327,7 @@ struct BaseCheckFunction
this->bigNG.copy(tem.bigNG);
this->edgeChannel.copy(tem.edgeChannel);
this->ad_check.copy(tem.ad_check);
this->Det_BQ.copy(tem.Det_BQ);
}
void print(std::string str)
{
@ -2247,6 +2338,7 @@ struct BaseCheckFunction
bigNG.print("bigNG");
edgeChannel.print("edgeChannel");
ad_check.print("ad_check");
Det_BQ.print("Det_BQ");
}
std::string GetInfo(std::string str)
{
@ -2257,6 +2349,7 @@ struct BaseCheckFunction
str123 += bigNG.GetInfo("bigNG");
str123 += edgeChannel.GetInfo("edgeChannel");
str123 += ad_check.GetInfo("ad_check");
str123 += Det_BQ.GetInfo("Det_BQ");
// str123 += "\n";
return str123;
}

@ -213,6 +213,7 @@ int ConfigInstance::Updata_Check(Json::Value json_value)
int ConfigInstance::SetStatus(int nConfigType)
{
std::lock_guard<std::mutex> lock(mutex_status);
for (int i = 0; i < MAX_USER_COUNT; i++)
{
m_USER_ConfigUpdataStatusList[nConfigType][i] = true;

@ -22,62 +22,6 @@ ConfigManager::~ConfigManager()
{
}
std::vector <std::string> QX_Result_Names =
{
"OK",
"AD_YX",
"X_Line",
"Y_Line",
"fangge",
"Rubbing_Mura",
"Broken_line",
"ZARA",
"MTX",
"POL_Cell",
"Bright_Point",
"Dark_Point",
"BLack_Point",
"White_Point",
"Scratch",
"Weak_Bright_Mura",
"No_Label",
"Bright_Mura_Exe",
"Sweak_Line_Dark",
"STEAM_POCKET",
"Dirty",
"other",
"Cell_W",
"Cell_B",
"LackPol"};
std::vector <std::string> QX_Result_Code =
{
"P1153",
"P6873",
"P3351",
"P3452",
"P3453",
"P1550",
"P3379",
"P1153",
"P1164",
"P1101",
"P1112",
"P1111",
"P1104",
"P1103",
"P1557",
"P1654",
"P2833",
"P1549",
"P1204",
"P2534",
"P2534",
"P1101",
"P1103",
"P1104",
"P8001",
};
int ReadFlawCodeConfig(std::string json_path)
{
m_FlawCodeList.erase(m_FlawCodeList.begin(), m_FlawCodeList.end());
@ -203,6 +147,7 @@ int ConfigManager::UpdateConfig()
if (!std::filesystem::exists(strConfigPath)) {
strConfigPath = m_strConfigRootPath + "/param.json";
}
printf("read param %s\n", strConfigPath.c_str());
std::shared_ptr<ConfigBase> temConfig = ConfigBase::GetInstance();
re = LoadParamConfig(temConfig, strConfigPath);
if (re != 0)
@ -218,7 +163,12 @@ int ConfigManager::UpdateConfig()
printf("Error >>>> camear Name is empty \n");
continue;
}
Config_instances_[p.commonCheckConfig.baseConfig.strCamearName] = temConfig;
auto it = Config_instances_.find(p.commonCheckConfig.baseConfig.strCamearName);
if (it != Config_instances_.end() && it->second) {
LoadParamConfig(it->second, strConfigPath);
} else {
Config_instances_[p.commonCheckConfig.baseConfig.strCamearName] = temConfig;
}
}
// getchar();

@ -32,7 +32,7 @@ void CommonParamToCheckConfigJson::toObjectFromValue(Json::Value root)
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Json::Value rootvalue;
std::string err;
// std::cout << strJson << std::endl;
std::cout << strJson << std::endl;
auto nSize = strJson.size();
if (reader->parse(strJson.c_str(), strJson.c_str() + nSize, &rootvalue, &err))
{
@ -49,6 +49,7 @@ void CommonParamToCheckConfigJson::toObjectFromValue(Json::Value root)
{
_config.baseConfig.strConfigVersion = "NULL";
}
_config.baseConfig.image_widht = value["image_widht"].asInt();
_config.baseConfig.Image_height = value["Image_height"].asInt();
_config.baseConfig.bDrawShieldRoi = value["bDrawShieldRoi"].asBool();
@ -559,6 +560,27 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
{
function.f_LDConfig.fDP_IOU = value_f["form"]["LD_Det"]["DP_IOU"].asFloat();
}
if (value_f["form"]["LD_Standard"]["LD_Area"])
{
function.f_LDConfig.fLD_Area = value_f["form"]["LD_Standard"]["LD_Area"].asFloat();
}
if (value_f["form"]["LD_Standard"]["LD_En"])
{
function.f_LDConfig.fLD_En = value_f["form"]["LD_Standard"]["LD_En"].asFloat();
}
if (value_f["form"]["LD_Standard"]["LD_HJ"])
{
function.f_LDConfig.fLD_HJ = value_f["form"]["LD_Standard"]["LD_HJ"].asFloat();
}
if (value_f["form"]["LD_Standard"]["LD_Len"])
{
function.f_LDConfig.fLD_Len = value_f["form"]["LD_Standard"]["LD_Len"].asFloat();
}
if (value_f["form"]["LD_Standard"]["bUseLD_Standard"])
{
function.f_LDConfig.bUseLD_Standard = value_f["form"]["LD_Standard"]["bUseLD_Standard"].asBool();
}
}
else
{
@ -969,7 +991,6 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
{
function.f_AD_Check.S_standard_3s.len = value_f["form"]["AD_S_Standard"]["AD_3S_Len"].asFloat();
}
if (value_f["form"]["AD_S_Standard"]["AD_2S_Area"])
{
function.f_AD_Check.S_standard_2s.area = value_f["form"]["AD_S_Standard"]["AD_2S_Area"].asFloat();
@ -978,7 +999,6 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
{
function.f_AD_Check.S_standard_2s.len = value_f["form"]["AD_S_Standard"]["AD_2S_Len"].asFloat();
}
if (value_f["form"]["AD_S_Standard"]["AD_1S_Area"])
{
function.f_AD_Check.S_standard_1s.area = value_f["form"]["AD_S_Standard"]["AD_1S_Area"].asFloat();
@ -1010,7 +1030,6 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
{
function.f_AD_Check.analysis_s.bOpen = value_f["form"]["AD_Check_S"]["Open"].asBool();
}
if (value_f["form"]["AD_Check_S"]["S_value"])
{
function.f_AD_Check.analysis_s.Check_s_Value = value_f["form"]["AD_Check_S"]["S_value"].asInt();
@ -1023,6 +1042,15 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
{
function.f_AD_Check.analysis_s.Check_s_Num = value_f["form"]["AD_Check_S"]["Num"].asInt();
}
if (value_f["form"]["AD_Check_IOU"]["AD_IOU"])
{
function.f_AD_Check.analysis_iou.AD_IOU = value_f["form"]["AD_Check_IOU"]["AD_IOU"].asFloat();
}
if (value_f["form"]["AD_Check_IOU"]["AD_Expand"])
{
function.f_AD_Check.analysis_iou.AD_Expand = value_f["form"]["AD_Check_IOU"]["AD_Expand"].asFloat();
}
}
else
{
@ -1443,6 +1471,21 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
_config.ad_check.Init();
}
}
// 标签配置参数
if ("Det_BQ" == strCode)
{
auto value_f = value;
// std::cout << value_f << std::endl;
// getchar();
_config.Det_BQ.bBQ_AI_Det = value_f["isOpen"].asBool();
{
if (value_f["form"]["bShield_BQ"]["BQ_expand"])
{
_config.Det_BQ.BQ_expand = value_f["form"]["bShield_BQ"]["BQ_expand"].asInt();
}
}
// _config.edgeDet.print("edgeDet");
// getchar();
}
return 0;
}

@ -0,0 +1,50 @@
#ifndef _MEM_MONITOR_H_
#define _MEM_MONITOR_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
// 获取当前进程 RSS (物理内存) 使用量,单位 MB
static inline long getRSS_KB()
{
long rss = 0;
FILE *fp = fopen("/proc/self/status", "r");
if (!fp) return -1;
char line[256];
while (fgets(line, sizeof(line), fp))
{
if (strncmp(line, "VmRSS:", 6) == 0)
{
// 格式: "VmRSS: 12345 kB"
const char *p = line + 6;
while (*p == ' ' || *p == '\t') p++;
rss = atol(p);
break;
}
}
fclose(fp);
return rss;
}
// 获取当前进程内存使用,打印格式化日志
static inline void printMemUsage(const char *tag, const char *extra)
{
long rss = getRSS_KB();
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
printf("[MEM] %-30s | VmRSS: %6ld MB | maxRSS: %6ld MB | %s\n",
tag, rss / 1024, usage.ru_maxrss / 1024, extra ? extra : "");
}
// 获取产品检测 pipeline 队列深度信息快照
#define MEM_LOG(tag, fmt, ...) \
do { \
long _rss = getRSS_KB(); \
printf("[MEM] %-30s | VmRSS: %6ld MB | " fmt "\n", tag, _rss / 1024, ##__VA_ARGS__); \
} while(0)
#endif // _MEM_MONITOR_H_

@ -3,6 +3,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "MemMonitor.h"
#include <unistd.h>
#include <string>
#include "CheckUtil.hpp"
@ -900,6 +901,7 @@ void deal::GetDealResultToQueu()
mutex_Result_list.lock();
m_Result_list.push(checkResult);
mutex_Result_list.unlock();
m_nTotalResultsDrained.fetch_add(1); // ✅ 更新计数器
}
else
{
@ -1341,11 +1343,65 @@ int deal::CheckFileImg()
products[i].print("产品" + std::to_string(i + 1));
}
// getchar();
printMemUsage("CheckFileImg-开始处理", "");
for (size_t i = 0; i < products.size(); i++)
{
char buf[128];
snprintf(buf, sizeof(buf), "产品[%zu/%zu] %s 处理前", i+1, products.size(), products[i].product_id.c_str());
printMemUsage(buf, "");
CheckProduct(products[i]); // 处理单个产品
snprintf(buf, sizeof(buf), "产品[%zu/%zu] %s 处理后", i+1, products.size(), products[i].product_id.c_str());
printMemUsage(buf, "");
}
// ✅ 修复:所有产品处理完成后,通知后台线程退出
printf("\n>>> 所有产品处理完成,正在停止后台线程...\n");
// 1. 通知产品读图线程退出
m_bProductReadExit = true;
m_productTaskCV.notify_all();
m_checkNotifyCV.notify_all();
// 2. 通知结果处理线程退出
m_bExit = true;
cv.notify_all();
// 3. 等待产品读图线程结束
for (auto &thread : m_productReadThreads)
{
if (thread && thread->joinable())
{
thread->join();
}
}
m_productReadThreads.clear();
// 4. 等待结果处理线程结束
for (auto &thread : ptr_ResultthreadList)
{
if (thread && thread->joinable())
{
thread->join();
}
}
ptr_ResultthreadList.clear();
// 5. 等待 GetResult 线程结束
if (ptr_GetResultthread && ptr_GetResultthread->joinable())
{
ptr_GetResultthread->join();
}
ptr_GetResultthread.reset();
// 6. 等待 DealImg 线程结束(如果存在)
if (ptr_DealImgthread && ptr_DealImgthread->joinable())
{
ptr_DealImgthread->join();
}
ptr_DealImgthread.reset();
printf(">>> 所有后台线程已停止,程序退出.\n");
printf("\n");
// TODO: 后续实现具体功能
@ -1386,6 +1442,11 @@ int deal::CheckProduct(const LoadProductImages &product)
m_ProductFailedList.clear();
m_nCheckNotifiedCount = 0;
// ✅ 修复内存泄漏清理上一个产品累积的原始图像数据cv::Mat
// 注意m_DetResult 是统计结果(轻量字符串/数字),不清除,跨产品累积
m_ImageInfoList.clear();
m_productIdList.clear();
// 3. 打印产品信息
printf("\n");
printf("╔══════════════════════════════════════════════════════════\n");
@ -1437,6 +1498,53 @@ int deal::CheckProduct(const LoadProductImages &product)
SendCheckImg(info, IN_IMG_Status_End, true);
printf("\n");
// ✅ 流控:等待 Run 线程处理完当前产品 + 结果全部保存完毕,再继续下一个产品
// 两步确认:
// 第1步 - 等 GetResultThread 把本产品所有结果从 m_CheckResultList 取出
// 第2步 - 等 ResultThread 把 m_Result_list 中的结果全部保存、释放内存
{
int startDrained = m_nTotalResultsDrained.load();
int expectedResults = (int)totalImgNum;
int maxWaitMs = 600000; // 最多等待10分钟
int waitedMs = 0;
// 第1步等结果被消费
printf("[流控-1] 等待产品 %s 的 %d 个检测结果被消费...\n", product.product_id.c_str(), expectedResults);
while (m_nTotalResultsDrained.load() - startDrained < expectedResults && waitedMs < maxWaitMs)
{
usleep(100000);
waitedMs += 100;
}
printf("[流控-1] 已完成,已消费 %d 个结果\n", m_nTotalResultsDrained.load() - startDrained);
// 第2步等结果全部保存完毕m_Result_list 排空)
printf("[流控-2] 等待结果保存完毕、内存释放...\n");
waitedMs = 0;
while (waitedMs < maxWaitMs)
{
bool queueEmpty;
{
std::lock_guard<std::mutex> lk(mutex_Result_list);
queueEmpty = m_Result_list.empty();
}
if (queueEmpty)
{
// 再等一小会确保最后一批 I/O 完成
usleep(500000);
break;
}
usleep(100000);
waitedMs += 100;
if (waitedMs % 10000 == 0)
{
size_t sz;
{ std::lock_guard<std::mutex> lk(mutex_Result_list); sz = m_Result_list.size(); }
printf("[流控-2] 等待中... 结果队列剩余 %zu 个\n", sz);
}
}
printf("[流控] 产品 %s 完全处理完毕,内存已释放,继续下一个产品\n\n", product.product_id.c_str());
}
// 6. 打印统计(不清理线程,继续下一个产品)
PrintProductStatistics(product);
// getchar();

@ -799,6 +799,7 @@ public:
std::mutex m_checkNotifyMutex; // 通知队列锁
std::condition_variable m_checkNotifyCV; // 通知条件变量
std::atomic<int> m_nCheckNotifiedCount{0}; // 已通知数量
std::atomic<int> m_nTotalResultsDrained{0}; // ✅ 已被消费的检测结果总数
std::set<std::string> m_productIdList; // 产品ID列表
};

@ -245,10 +245,5 @@ int main(int argc, char *argv[])
signal(SIGINT, handler);
test.start();
while (true)
{
usleep(10 * 1000);
}
return 0;
}

Loading…
Cancel
Save