Compare commits

...

89 Commits

Author SHA1 Message Date
liusiyang aae5b72558 update 127的pol_cell算白点
2 days ago
liusiyang 8a8492472d update 过滤不限制通道
2 days ago
liusiyang 7ef04b6234 feat 主通道为空,使用次通道走老逻辑v2
1 week ago
liusiyang badc3fa4c6 feat 完善黑白点过滤
1 week ago
liusiyang 3741c2282d feat 完善盲失过滤规则
1 week ago
liusiyang e80cae3e60 feat 添加盲失,黑白点过滤参数配置
1 week ago
liusiyang 87746fc05d feat 分类完成后添加去重
3 weeks ago
liusiyang 14c6066ff9 update 添加开关控制是否绘制存图
3 weeks ago
liusiyang bbde51f4cc v1.3.11
3 weeks ago
liusiyang 62db151272 update 调整关联通道入列位置
3 weeks ago
liusiyang 14c02a7a98 Revert "feat 主通道为空,使用次通道走老逻辑"
3 weeks ago
liusiyang 415952f0bc update 弱线检测合并边缘模型结果
3 weeks ago
liusiyang e19bdc7d53 v1.3.10
3 weeks ago
liusiyang 70ca852dfa fix 标签模型出的缺陷能量计算不对
3 weeks ago
xiewenji 68f0d4aa45 增加弱线检测 xwj
3 weeks ago
liusiyang 01247bccb9 feat 主通道为空,使用次通道走老逻辑
4 weeks ago
liusiyang ee98ece5ff feat 开放关联通道缺陷配置
1 month ago
liusiyang 0bd86d0acb v1.3.9
1 month ago
liusiyang cd8aa0f492 update 修改脏污过滤流程
1 month ago
liusiyang ab3abb8fa8 feat 初步添加关联通道分析
2 months ago
liusiyang 23a8043d32 feat 添加关联通道参数配置
2 months ago
liusiyang da684c8a7f fix 对齐参数读取防御判断
2 months ago
liusiyang 46dac481ed v1.3.8
2 months ago
liusiyang 1ef5102b93 feat 添加存储路径配置
2 months ago
liusiyang 95326fd8f6 update AI模型推理添加日志也异常捕获
2 months ago
liusiyang 7b8500dd29 fix ad数量距离分析跳过未配置
2 months ago
liusiyang c49bce980a update 新增监控小图AI测试
2 months ago
liusiyang 85460ca7ac update 暗点Bind日志整理,拆分主通道结果
2 months ago
liusiyang 584daf6e0b feat 添加区域1和s参数选择通道分析
2 months ago
liusiyang 4aa7b7feb3 update 清理Function_AD_Check
2 months ago
liusiyang 8e6a661e19 fix 暗点参数读取bug
2 months ago
liusiyang bc0c7eaf3e feat 初步完成AD绑定分析
2 months ago
liusiyang 5b7d9f11e8 feat 初步添加AD绑定分析
2 months ago
liusiyang 44b9b8eae5 feat 添加午后处理AI_OUT存图
2 months ago
liusiyang d9277a9c6e feat 添加定时小图测试
3 months ago
liusiyang a2030f007f update 部分结果图添加宏定义控制
3 months ago
liusiyang cb1df89aab fix 结果图存储bug
3 months ago
liusiyang d0522d70fb v1.3.6
3 months ago
liusiyang c4747748a0 feat 添加宏定义控制结果图返回
3 months ago
liusiyang 40c4b82870 feat 添加-testAIDet单独测试defect.engine
3 months ago
liusiyang c35c61fe38 feat 添加L0DP测试存图
3 months ago
liusiyang d5a2458036 update 限制推理并发数
3 months ago
liusiyang e4436f7028 update 修改blod上限1000
3 months ago
liusiyang 38a9739b7d feat YX添加面积过滤YS
3 months ago
liusiyang 73193e70dd v1.3.5
3 months ago
liusiyang e5fb359e02 fix 使用参数配置BQ检测开关
3 months ago
liusiyang 857057166f fix -tag离线程序mask为空bug
4 months ago
liusiyang 9d3164ebcb v1.3.4
4 months ago
liusiyang 84da7c79cd feat AI_Mask合并BQ检测
4 months ago
liusiyang 085e06bb87 feat 离线程序添加-tag
4 months ago
liusiyang 530ba7b3bf update 整理标签存图路径
4 months ago
liusiyang a5bfe40bee update 标签检测存图添加参数配置
4 months ago
liusiyang 4092ade847 update 标签检测分块(v1.3.3)
4 months ago
liusiyang d3a93f9fbe fix 参数读取bug
4 months ago
liusiyang 6c5f6a9d55 fix 根据参数配置进行BQ检测(v1.3.2)
4 months ago
liusiyang 53878afe4f update 修改标签检测模型名,精简参数设置
4 months ago
liusiyang 0b420a8476 v1.3.0
4 months ago
liusiyang c1b8cda63e update 批量跑同步执行,避免内存溢出
4 months ago
liusiyang 8274481bd6 update 日志文件分小时存储
4 months ago
liusiyang 61bd08fdf6 update 改为全部日志存储
4 months ago
liusiyang 548fa6f472 v1.2.9
4 months ago
liusiyang 5b8e4b7999 feat AD_YX添加HJ计算
4 months ago
liusiyang c1c6115c8a fix Detect可能越界崩溃
4 months ago
liusiyang 0dfbca8934 feat 添加AI_defect_Tag检测模型
4 months ago
liusiyang 768a227910 feat 初步添加BQ模型检测
4 months ago
liusiyang eb2cf825ce feat 开放标签扩/缩参数
4 months ago
liusiyang 8610481adb fix 异物范围过滤
4 months ago
liusiyang 63385495a1 feat LD新增检测(v1.2.7)
4 months ago
liusiyang 2f5f0e1589 update 更改线的二次判断,异物添加范围
4 months ago
liusiyang 9996b5a228 feat 开放暗点同位置判断IOU(v1.2.6)
4 months ago
liusiyang 49390e31bb fix 分类输入图片resize(v1.2.5)
4 months ago
liusiyang a9f6d659bd fix 删除重复定义
5 months ago
liusiyang f3ebf08fd6 Merge branch 'dev_lsy' of https://gitea.aidlux.getcharzp.cn/liusiyang/BOE_FOG_DETECT into dev_lsy
5 months ago
liusiyang 74f604ca65 update ngimg日志存储添加日期
5 months ago
liusiyang 8c5bcd7c9f feat density计算
5 months ago
liusiyang 4c36c7534c update 标签外扩5pix
5 months ago
liusiyang aaac47868f Revert "update 云平台改为单显卡" fog使用双显卡
5 months ago
liusiyang e47a221978 update 云平台改为单显卡
5 months ago
liusiyang c321a118d5 update 更新版本号
5 months ago
liusiyang eba708f32a fix 3s暗点漏检
5 months ago
liusiyang b535bc481e fix 异物漏检
5 months ago
liusiyang 8728bbdb68 update 标签外扩2pix
5 months ago
liusiyang 29086815bc fix 同位置3s暗点报出
5 months ago
liusiyang 036d19bbe6 Merge branch 'dev_lsy' of https://gitea.aidlux.getcharzp.cn/liusiyang/BOE_FOG_DETECT into dev_lsy
5 months ago
liusiyang 0857847444 fix 更改灰阶计算
5 months ago
liusiyang c1f4a2b451 update 同步cell参数延迟更改
5 months ago
liusiyang 0afe6c80a6 fix LD漏检
5 months ago
liusiyang 18a1394759 feat 添加RGB-HGRAY检测
5 months ago
liusiyang 546ce62fe6 fix AI_Time限制绘图
5 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
@ -191,6 +192,8 @@ public:
std::shared_ptr<AIModel_Base> AI_defect_MarkLine; // 缺pol检测
std::shared_ptr<AIModel_Base> AI_defect_Edge_QX; // 边缘缺陷检测
std::shared_ptr<AIModel_Base> AI_defect_RLine_512; // 弱线检测模型 512*512
private:
// 模型是否都加载完成了。
bool m_bInitSucc = false;

@ -265,14 +265,15 @@ int AIModel_Impl::AIDet(const cv::Mat &inImg, cv::Mat &outimg)
{
outimg.create(m_pNode_output_0->height, m_pNode_output_0->width, outType);
}
// 推理前清零输出:任何一步失败时,保证输出全黑而非未初始化垃圾数据
outimg.setTo(0);
uchar *p_outdata_1 = (uchar *)outimg.data;
if (!m_bInitSuccess)
{
return 2;
}
AI_Det_In_1_Out_1(m_pNode_input_0, m_pNode_output_0, p_indata_0, p_outdata_1);
return 0;
return AI_Det_In_1_Out_1(m_pNode_input_0, m_pNode_output_0, p_indata_0, p_outdata_1);
}
int AIModel_Impl::AIDet(const cv::Mat &inImg, cv::Mat &outimg0, cv::Mat &outimg1)
@ -553,38 +554,89 @@ int AIModel_Impl::GetEngineInfo1(std::shared_ptr<GPU_Engine> &pgpuengine)
int AIModel_Impl::AI_Det_In_1_Out_1(Node_Config *pConfig_in, Node_Config *pConfig_out, const unsigned char *p_indata_0, unsigned char *p_outdata_1)
{
// printf("=== s1 ");
std::shared_ptr<Det_GPU_Stram> pdetStream;
if (GetStream(pdetStream) != 0 || !pdetStream)
{
return -1;
}
// printf("=== s2 ");
std::lock_guard<std::mutex> lock(pdetStream->AI_mutex);
// printf(" ss g %d s %d -- ", pdetStream->nGPUIdx, pdetStream->cuda_stream->nstreamIdx);
// // 设置 显卡ID
// 设置 显卡ID
if (!EnsureCudaDevice(pdetStream->nGPUIdx))
{
return -1;
}
cudaMemcpyAsync(pdetStream->cuda_stream->pstreamNode_input_0->gpu_ImgData,
p_indata_0, pConfig_in->ucharsize, cudaMemcpyHostToDevice, pdetStream->cuda_stream->stream);
// // // 在显存中 图片数据从 uchar 转到 float
Cuda_ucharToFloat_stream((unsigned char *)pdetStream->cuda_stream->pstreamNode_input_0->gpu_ImgData,
(float *)pdetStream->cuda_stream->pstreamNode_input_0->gpu_buffers, pConfig_in->datalength, pdetStream->cuda_stream->stream);
cudaStream_t stream = pdetStream->cuda_stream->stream;
void *gpu_in_ImgData = pdetStream->cuda_stream->pstreamNode_input_0->gpu_ImgData;
void *gpu_in_buffers = pdetStream->cuda_stream->pstreamNode_input_0->gpu_buffers;
void *gpu_out_buffers = pdetStream->cuda_stream->pstreamNode_output_0->gpu_buffers;
void *gpu_out_ImgData = pdetStream->cuda_stream->pstreamNode_output_0->gpu_ImgData;
pdetStream->cuda_stream->context->setTensorAddress(pConfig_in->name.c_str(), pdetStream->cuda_stream->pstreamNode_input_0->gpu_buffers);
pdetStream->cuda_stream->context->setTensorAddress(pConfig_out->name.c_str(), pdetStream->cuda_stream->pstreamNode_output_0->gpu_buffers);
cudaError_t cerr;
pdetStream->cuda_stream->context->enqueueV3(pdetStream->cuda_stream->stream);
// 推理前先把输出显存清零:任何一步失败时,保证输出全黑而非未初始化垃圾数据
cerr = cudaMemsetAsync(gpu_out_ImgData, 0, pConfig_out->ucharsize, stream);
if (cerr != cudaSuccess)
{
std::fprintf(stderr, "[AIEngine] cudaMemsetAsync(out) failed: %s\n", cudaGetErrorString(cerr));
return -1;
}
Cuda_FloatTouchar_stream((float *)pdetStream->cuda_stream->pstreamNode_output_0->gpu_buffers,
(unsigned char *)pdetStream->cuda_stream->pstreamNode_output_0->gpu_ImgData, pConfig_out->datalength, pdetStream->cuda_stream->stream);
cudaMemcpyAsync(p_outdata_1, pdetStream->cuda_stream->pstreamNode_output_0->gpu_ImgData,
pConfig_out->ucharsize, cudaMemcpyDeviceToHost, pdetStream->cuda_stream->stream);
cudaStreamSynchronize(pdetStream->cuda_stream->stream);
// printf("**ee g %d s %d ** \n", pdetStream->nGPUIdx, pdetStream->cuda_stream->nstreamIdx);
// 输入数据拷贝到显存
cerr = cudaMemcpyAsync(gpu_in_ImgData, p_indata_0, pConfig_in->ucharsize, cudaMemcpyHostToDevice, stream);
if (cerr != cudaSuccess)
{
std::fprintf(stderr, "[AIEngine] cudaMemcpyAsync(in) failed: %s\n", cudaGetErrorString(cerr));
return -1;
}
// 在显存中 图片数据从 uchar 转到 float
Cuda_ucharToFloat_stream((unsigned char *)gpu_in_ImgData,
(float *)gpu_in_buffers, pConfig_in->datalength, stream);
if (!pdetStream->cuda_stream->context->setTensorAddress(pConfig_in->name.c_str(), gpu_in_buffers))
{
std::fprintf(stderr, "[AIEngine] setTensorAddress(in) failed\n");
return -1;
}
if (!pdetStream->cuda_stream->context->setTensorAddress(pConfig_out->name.c_str(), gpu_out_buffers))
{
std::fprintf(stderr, "[AIEngine] setTensorAddress(out) failed\n");
return -1;
}
if (!pdetStream->cuda_stream->context->enqueueV3(stream))
{
std::fprintf(stderr, "[AIEngine] enqueueV3 failed\n");
return -1;
}
// float 转到 uchar
Cuda_FloatTouchar_stream((float *)gpu_out_buffers,
(unsigned char *)gpu_out_ImgData, pConfig_out->datalength, stream);
cerr = cudaMemcpyAsync(p_outdata_1, gpu_out_ImgData,
pConfig_out->ucharsize, cudaMemcpyDeviceToHost, stream);
if (cerr != cudaSuccess)
{
std::fprintf(stderr, "[AIEngine] cudaMemcpyAsync(out) failed: %s\n", cudaGetErrorString(cerr));
return -1;
}
cerr = cudaStreamSynchronize(stream);
if (cerr != cudaSuccess)
{
std::fprintf(stderr, "[AIEngine] cudaStreamSynchronize failed: %s\n", cudaGetErrorString(cerr));
return -1;
}
// kernel 启动失败等异步错误必须通过 cudaGetLastError 捕获
cerr = cudaGetLastError();
if (cerr != cudaSuccess)
{
std::fprintf(stderr, "[AIEngine] async cuda error: %s\n", cudaGetErrorString(cerr));
return -1;
}
return 0;
}

@ -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_Tag.engine";
boe_config.strName = "tag";
boe_config.inputType = AIModel_Base::Input_CHW;
AI_defect_Tag->Init(boe_config);
}
// 字符 127cell
if (!AI_defect_127Cell)
{
@ -253,6 +264,17 @@ int AIFactory::InitALLAIModle(GPU_Config gupconfig)
boe_config.inputType = AIModel_Base::Input_CHW;
boe_config.strName = "Edge_QX";
AI_defect_Edge_QX->Init(boe_config);
}
// 弱线检测模型 512*512
if (!AI_defect_RLine_512)
{
AI_defect_RLine_512 = AIModel_Base::GetInstance();
AIModel_Base::AIModelRun_Config boe_config;
boe_config.gpuconfig.copy(gupconfig);
boe_config.strPath = "/home/aidlux/BOE/UseModel_FOG/FOG_L127line.engine";
boe_config.strName = "RLine_512";
boe_config.inputType = AIModel_Base::Input_CHW;
AI_defect_RLine_512->Init(boe_config);
}
m_bInitSucc = true;
return 0;

@ -45,7 +45,13 @@ public:
AI_Edge_QX_Det(/* args */);
~AI_Edge_QX_Det();
int Detect(const cv::Mat &img, cv::Mat &detmask, DetConfig *pDetConfig,std::shared_ptr<DetLog> pdetlog);
/// @brief 边缘缺陷检测,检测结果根据配置参数合并到 mask
/// @param img 检测原图
/// @param detmask 合并目标 mask(主检测)
/// @param pDetConfig 检测参数
/// @param pdetlog 日志
/// @param pDetmask2 可选:第二个合并目标 mask(如弱线检测mask),按与主检测相同规则合并
int Detect(const cv::Mat &img, cv::Mat &detmask, DetConfig *pDetConfig, std::shared_ptr<DetLog> pdetlog, cv::Mat *pDetmask2 = nullptr);
private:
int SaveProcessImg(const cv::Mat &inImg, const cv::Mat &outImg, bool isALLzeros, DetConfig *pDetConfig, int idx = 0, int nruntype = 0);

@ -10,8 +10,8 @@ enum ERR_DOT_TYPE_ENUM
#define Value_ERR_TYPE_2 128
//-----------------------sxg added
#define _MAX_ERROR_SCAN_LINE_PER_ROW 300
#define _MAX_ERROR_DOT_BLOB 80
#define _MAX_ERROR_SCAN_LINE_PER_ROW 1000
#define _MAX_ERROR_DOT_BLOB 1000
#define _MAX_MACRO_COUNT 4
#define _MAX_ERR_CLASS ERR_TYPE_COUNT

@ -26,7 +26,7 @@
// resize 图片的 宽度
#define RESIZE_IMAGE_WIDTH 1680
// #define AI_Time
// 结果图保存/绘制开关:已由 SAVE_RESIMG 宏改为 base 层配置项“是否存图绘制”(baseCheckData 中 itemCode=bDrawAndSave 的 isOpen),运行时生效,无需再改此处宏
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);

@ -46,8 +46,8 @@ public:
struct Config_Update
{
bool bUpdate = false;
bool wait_DP= false;
bool wait_UP= false;
bool wait_DP = false;
bool wait_UP = false;
};
// AI MaskImg 的相关使用的参数信息。
@ -173,6 +173,8 @@ 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标签检测要用到的一些 参数 。
std::shared_ptr<Image_AI_Det_Result> RLine_512_DetAIResult; // 弱线512模型 检测要用到的一些 参数 。
// 结果图片
cv::Mat resultImg;
@ -204,7 +206,6 @@ public:
Config_Update config_update;
public:
DetStep m_step = DetStep_NotDet;
std::string strBaseInfo = "";

@ -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,11 @@ struct BQ_Result
{
int nresult = 1;
bool bShield_BQ = true; // 是否屏蔽标签
std::vector<cv::Rect> pBQ_roiList; // 标签的区域
bool bBQ_AI_Det = false; // 是否启用标签独立AI检测
bool bsaveAll = false; // 是否保存所有的检测结果
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

@ -107,6 +107,15 @@ public:
int UpdateConfig(std::string strcameraName);
int DrawResult(std::shared_ptr<ImageAllResult> pImageResult);
// 盲失联合判断:主通道(ms_main_channel)检出的盲失(Other)若在过滤通道(ms_filter_channels)同位置存在缺陷
// (不限制缺陷类型),则不算盲失(撤销NG)。同位置判断参考暗点联合分析:双方按 ms_expand 扩张后计算 IOU, 大于 ms_iou 认为同位置
int FilterMsBlindNG(std::shared_ptr<CameraResult> pCheck_Result);
// cell黑白点联合判断:主通道(cellbw_main_channel)检出的cell黑白点(Cell127 也算白点)在过滤通道
// (cellbw_filter_channels)同位置存在缺陷(不限制缺陷类型)时不算cell黑白点(撤销NG)
// 同位置判断参考暗点联合分析:双方按 cellbw_expand 扩张后计算 IOU, 大于 cellbw_iou 认为同位置
int FilterCellBwNG(std::shared_ptr<CameraResult> pCheck_Result);
private:
// 获取 缺陷在参数列表的位置
int GetParamidx();
@ -120,6 +129,9 @@ private:
// 获取缺陷 对应的AI输入 输出图片,
int GetAIDetImg(std::shared_ptr<ImageAllResult> pImageResult, cv::Point pcenter, cv::Mat &AI_InImg, cv::Mat &AI_OutImg);
// 绘制并保存结果图开关(base 配置 bDrawAndSave,替代 SAVE_RESIMG 宏)
bool NeedDrawAndSave();
int sendTask(std::shared_ptr<DrawInfo> task);
std::shared_ptr<ImageResultJudge::DrawInfo> GetTask();
@ -132,6 +144,39 @@ private:
int AnalysisResult_Pre(QX_ERROR_INFO_ *QX_info); // 弱化处理
// 撤销单个缺陷的NG结果:恢复为OK,移除对应NG小图并回退计数
int UndoNGResult(std::shared_ptr<ImageAllResult> pImageResult, int qxidx, QX_ERROR_INFO_ *QX_info);
// 跨通道同位置判断:当前缺陷(产品坐标系ROI)在过滤通道是否存在同位置缺陷(双方按 expand 扩张后计算IOU)
bool IsOverlapQxInFilterChannels(const std::vector<std::shared_ptr<ImageAllResult>> &imgResults,
const std::string &cur_channel_name,
const std::vector<std::string> &filter_channels,
const std::vector<int> &match_qx_types,
const cv::Rect &qx_product_roi,
int roi_expand, float iou_threshold,
const std::string &strTag,
std::shared_ptr<DetLog> plog);
// 判断盲失(产品坐标系ROI)在配置的过滤通道是否存在同位置缺陷(不限制类型,双方按 expand 扩张后计算IOU)
bool IsOverlapQxInMsFilterChannel(const std::vector<std::shared_ptr<ImageAllResult>> &imgResults,
const std::string &cur_channel_name,
const cv::Rect &qx_product_roi,
int roi_expand, float iou_threshold,
std::shared_ptr<DetLog> plog);
// 跨通道联合判断(通用):主通道中指定类型的NG缺陷,若在过滤通道同位置存在指定类型的缺陷,则撤销该NG
int FilterCrossChannelNG(std::shared_ptr<CameraResult> pCheck_Result,
const std::vector<std::string> &main_channels,
const std::vector<std::string> &filter_channels,
const std::vector<int> &main_qx_types,
const std::vector<int> &match_qx_types,
int roi_expand, float iou_threshold,
const std::string &strTag,
const std::string &strReason);
// 关联过滤源过滤:撤销与关联通道过滤源缺陷重叠的暗点NG结果(含单图判定的单点NG)
int FilterCorrelatedDirtyNG(std::shared_ptr<ImageAllResult> pImageResult);
ChannelCheckFunction *GetChannelFuntion(std::string strChannelName); // 获得 通道的检测功能
int ConfigTypeToQXAnalysis(int nconfigType);

@ -73,6 +73,9 @@ public:
// 获取检测库 状态信息 返回:CHECK_THREAD_RUN_STATUS
int GetStatus();
// 计算blob的密度
int CalBlobDensity_QX();
// 更新参数 pconfig 参数指针,nConfigType 需要更新的参数类型 返回:0 成功 其他异常
int UpdateConfig(void *pconfig, int nConfigType);
@ -190,6 +193,15 @@ private:
int GetBlob_QX();
// 获得 127cell blob
int GetBlob_127cell();
// 获得 RLine_512 blob
int GetBlob_RLine_512();
// 更新 RLine_512 mask 的 blob 行标记(合并边缘检测结果后刷新)
int UpdateBlobHFlag_RLine_512();
// 获得 BQ标签 blob
int GetBlob_BQ();
// BQ标签 AI 检测
int AI_Detect_BQ();
// 对AI mask图片进行 结果处理
int AIMaskDet();
@ -211,6 +223,8 @@ private:
int AI_Detect_QX();
// 127cell
int AI_Detect_127Cell();
// 弱线检测-512模型
int AI_Detect_RLine_512();
// 缺陷分类
int AI_QX_Class_Thread();
@ -237,12 +251,18 @@ private:
// 临时绘制结果
int DrawResult_Step_1();
// 绘制并保存结果图开关(base 配置 bDrawAndSave,替代 SAVE_RESIMG 宏)
bool NeedDrawAndSave();
// 参数缺陷类型转 结果参数类型
int AIClassTypeToConfigType(int nAIQXType, cv::Rect qx_roi);
// 更新成像精度
int UpdateImgageScale();
// 分类完成后按 bbox 完全相等去重(同一缺陷被多个 mask 来源各提取一次)
int DedupBlob_ByBBox();
// 把blob 汇总成 检测结果。
int BLobToDetResult();
// 二次计算缺陷面积

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

@ -18,7 +18,7 @@
#include <unordered_map> // unordered_map
#define RESULT_VERSION 31
#define MAX_BLOB_NUM 200
#define MAX_BLOB_NUM 1000
// 输入模型图片尺寸
#define SRCIMG_WIDTH 14200

@ -8,6 +8,8 @@
#include <mutex>
#include <thread>
#include <condition_variable>
#include <unordered_map>
#include <vector>
#include "CheckErrorCodeDefine.hpp"
#include "CheckConfigDefine.h"
#include "QX_Analysis.h"
@ -68,6 +70,90 @@ public:
}
};
struct AD_Channel_BD_Info_
{
cv::Rect roi;
cv::Point2f location_product_mm;
vector<string> s_name_2;
vector<string> s_name_3;
int s_type;
AD_Channel_BD_Info_()
{
roi = cv::Rect(0, 0, 0, 0);
location_product_mm = cv::Point2f(0, 0);
s_name_2.clear();
s_name_3.clear();
s_type = 0;
}
std::string GetInfo()
{
char buffer[256];
snprintf(buffer, sizeof(buffer), "roi:[%d,%d,%d,%d] location [%f %f] s_type:%d",
roi.x, roi.y, roi.width, roi.height, location_product_mm.x, location_product_mm.y, s_type);
std::string str = buffer;
for(int i = 0; i < s_name_2.size(); i++)
{
str += " s_name_2[" + s_name_2[i] + "]";
}
for(int i = 0; i < s_name_3.size(); i++)
{
str += " s_name_3[" + s_name_3[i] + "]";
}
return str;
}
};
struct AD_Channel_Stat_
{
int num_1s;
int num_2s;
int num_3s;
int num_2s_s;
int num_2s_m;
int num_3s_s;
int num_3s_m;
int product_AD_num;
float min_dis;
AD_Channel_Stat_()
{
num_1s = 0;
num_2s = 0;
num_3s = 0;
num_2s_s = 0;
num_2s_m = 0;
num_3s_s = 0;
num_3s_m = 0;
product_AD_num = 0;
min_dis = 9999999999;
}
};
// 关联通道缺陷信息(用于判断关联通道是否存在脏污(dirty)重叠缺陷)
struct Correlated_Qx_Info_
{
std::string channel_name; // 缺陷所属通道
std::string camera_name; // 缺陷所属相机
int config_qx_type; // 缺陷配置类型 CONFIG_QX_NAME_
int result_type; // 缺陷结果类型(仅用于日志/追溯)
cv::Rect product_roi; // 产品坐标系下的缺陷ROI
Correlated_Qx_Info_()
{
channel_name = "";
camera_name = "";
config_qx_type = -1;
result_type = -1;
product_roi = cv::Rect(0, 0, 0, 0);
}
std::string GetInfo()
{
char buffer[256];
snprintf(buffer, sizeof(buffer), "cam:%s channel:%s cfgType:%d resType:%d roi:[%d,%d,%d,%d]",
camera_name.c_str(), channel_name.c_str(), config_qx_type, result_type,
product_roi.x, product_roi.y, product_roi.width, product_roi.height);
return std::string(buffer);
}
};
public:
QX_Merge_Analysis();
~QX_Merge_Analysis();
@ -88,6 +174,19 @@ public:
bool Idx(int qxidx);
bool AddQxInfo(int qxidx, QX_Info qxinfo, std::shared_ptr<DetLog> plog, std::shared_ptr<DetLog> pchannelLog);
// 登记关联通道(非主/次通道)的过滤源缺陷信息(类型由 correlated_types 配置, 如脏污/暗点),供 AD 分析判断重叠缺陷
bool AddCorrelatedQxInfo(Correlated_Qx_Info_ qxinfo, std::shared_ptr<DetLog> plog);
// 判断缺陷类型是否为配置的关联过滤源类型(analysis_bind.correlated_types, 如 "Dirty_L0"/"Dirty_L1"/"Dirty_L2"/"AD")
// 配置为空时默认三种脏污; 登记与过滤两处共用此判定
bool IsCorrelatedSourceConfigType(int nconfigType);
// 关联通道过滤源重叠判断(供外部调用):当前缺陷ROI是否与关联通道过滤源缺陷(脏污/暗点等)重叠
bool IsDirtyOverlapInCorrelatedChannel(const std::string &cur_channel_name,
const std::string &cur_camera_name,
const cv::Rect &qx_product_roi,
std::shared_ptr<DetLog> plog);
bool setSendStatus(std::string cameraName, RUN_STATUS_ status);
bool waitComplete(int timeoutMs = 15000);
@ -115,7 +214,28 @@ private:
int Judge_OK_Config();
int Judge_NG_Config();
int Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx);
// pAllowChannels 非空时仅列表内通道参与分析(主通道未配置时的回退: 仅次通道);
// nullptr=不过滤;非空但为空列表=不做任何通道分析(直接返回)
int Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx, const std::vector<std::string> *pAllowChannels = nullptr);
int Analysis_AD_Bind(ALL_Qx_DataList *pALLTypeqxList, int qx_idx);
// 绑定分析的主通道是否未配置(2s 或 3s 的主通道列表为空);
// 只按配置判断,与本次产品是否检出缺陷无关(良品主通道本来就没暗点,按缺陷判断会频繁误回退)
bool IsADBindMainChannelConfigEmpty();
// 绑定分析的次通道列表(second_channel_2s + second_channel_3s, 去重并过滤空名)
std::vector<std::string> GetADBindSecondChannels();
// 关联过滤源过滤:移除与关联通道过滤源缺陷重叠的缺陷,使其不参与后续数量/距离/S分析
int FilterCorrelatedDirtyQx(ALL_Qx_DataList *pALLTypeqxList);
// 判断当前缺陷是否与关联通道(correlated_channels)中的过滤源缺陷(correlated_types)重叠
bool IsOverlapDirtyInCorrelatedChannel(const std::vector<std::string> &correlated_channels,
const std::string &cur_channel_name,
const std::string &cur_camera_name,
const cv::Rect &qx_product_roi,
int roi_expand,
float iou_threshold,
std::shared_ptr<DetLog> plog);
ChannelCheckFunction *GetChannelFuntion(std::string strChannelName);
@ -140,6 +260,13 @@ private:
std::vector<AD_Channel_Info_> AD_list;
std::unordered_map<string, vector<AD_Channel_BD_Info_>> AD_list_bind;
// 关联通道缺陷缓存:channel_name -> 缺陷列表(产品坐标系)
std::unordered_map<std::string, std::vector<Correlated_Qx_Info_>> m_correlatedQxMap;
std::mutex mtx_correlatedQx;
private:
};

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

@ -122,18 +122,18 @@ std::string AI_Edge_QX_Det::GetImgSaveName(int flag)
return str_root;
}
int AI_Edge_QX_Det::Detect(const cv::Mat &img, cv::Mat &detmask, DetConfig *pDetConfig, std::shared_ptr<DetLog> pdetlog)
int AI_Edge_QX_Det::Detect(const cv::Mat &img, cv::Mat &detmask, DetConfig *pDetConfig, std::shared_ptr<DetLog> pdetlog, cv::Mat *pDetmask2)
{
static int n_count = 0;
int nRuntype = 0; // 0:检测 1:测试。。。。。
m_pdetlog = pdetlog;
if (pDetConfig->ninstruct == 767)
{
// nRuntype = 1;
nRuntype = 1;
}
else if (pDetConfig->ninstruct == 768)
{
// nRuntype = 2;
nRuntype = 2;
}
bool bdege = false;
@ -169,6 +169,14 @@ int AI_Edge_QX_Det::Detect(const cv::Mat &img, cv::Mat &detmask, DetConfig *pDet
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "AI_Edge_QX_Det ", "nruntype %d %d\n", pDetConfig->pdege_Det_config->bsave_all, pDetConfig->pdege_Det_config->bsave_qx);
// 第二个合并目标(如弱线检测mask):尺寸与主检测mask一致时才参与合并
bool bMergeDetmask2 = false;
if (pDetmask2 != nullptr && !pDetmask2->empty() && pDetmask2->size() == detmask.size())
{
bMergeDetmask2 = true;
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "AI_Edge_QX_Det ", "extra detmask merge on %s", pDetConfig->strCamChannel.c_str());
}
if (bdege || nRuntype != 0)
{
showImg = img.clone();
@ -206,6 +214,16 @@ int AI_Edge_QX_Det::Detect(const cv::Mat &img, cv::Mat &detmask, DetConfig *pDet
continue;
}
if (bdege || nRuntype != 0)
{
std::string strSaveDir = "/home/aidlux/BOE/FOG/AI_Edge_Detect/";
CheckUtil::CreateDir(strSaveDir);
std::string roi_ext = "[" + std::to_string(r.x) + "," + std::to_string(r.y) + "]";
std::string strSavePath_in = strSaveDir + pDetConfig->strCamChannel + "_" + roi_ext + "_edge_in.png";
std::string strSavePath_out = strSaveDir + pDetConfig->strCamChannel + "_" + roi_ext + "_edge_out.png";
cv::imwrite(strSavePath_in, detimg);
cv::imwrite(strSavePath_out, outMask);
}
bool isAllZero = true;
if (cv::countNonZero(outMask) > 0)
{
@ -295,14 +313,27 @@ int AI_Edge_QX_Det::Detect(const cv::Mat &img, cv::Mat &detmask, DetConfig *pDet
if (pDetConfig->pdege_Det_config->type == Function_Edge_Det::Edge_type_Add)
{
outMask(smallImg_roi).copyTo(detmask(mask_roi), outMask(smallImg_roi));
// 第二个合并目标(如弱线检测mask):与主检测mask相同规则合并
if (bMergeDetmask2)
{
outMask(smallImg_roi).copyTo((*pDetmask2)(mask_roi), outMask(smallImg_roi));
}
}
else if (pDetConfig->pdege_Det_config->type == Function_Edge_Det::Edge_type_And)
{
cv::bitwise_and(detmask(mask_roi), outMask(smallImg_roi), detmask(mask_roi));
if (bMergeDetmask2)
{
cv::bitwise_and((*pDetmask2)(mask_roi), outMask(smallImg_roi), (*pDetmask2)(mask_roi));
}
}
else
{
outMask(smallImg_roi).copyTo(detmask(mask_roi));
if (bMergeDetmask2)
{
outMask(smallImg_roi).copyTo((*pDetmask2)(mask_roi));
}
}
}
}

@ -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;
@ -340,6 +341,26 @@ int CameraCheckAnalysisy::ResultParamJudge()
int re = m_imageResultJudge.ResultJudge(m_pCheck_Result->ImageALLDetResultList.at(imgidx));
long t2 = CheckUtil::getcurTime();
}
// 盲失联合判断:主通道(ms_main_channel)检出的盲失若在过滤通道(ms_filter_channels)同位置存在缺陷(不限制类型),则不算盲失(撤销NG)
{
int nMsFilterNum = m_imageResultJudge.FilterMsBlindNG(m_pCheck_Result);
if (nMsFilterNum > 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, "ResultParamJudge", "盲失联合判断: 过滤通道同位置存在缺陷, 过滤盲失NG %d 个", nMsFilterNum);
}
}
// cell黑白点联合判断:主通道(cellbw_main_channel)检出的cell黑白点若在过滤通道(cellbw_filter_channels)同位置
// 存在缺陷(不限制类型),则不算cell黑白点(撤销NG)
{
int nCellBwFilterNum = m_imageResultJudge.FilterCellBwNG(m_pCheck_Result);
if (nCellBwFilterNum > 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, "ResultParamJudge", "cell黑白点联合判断: 过滤通道同位置存在缺陷, 过滤NG %d 个", nCellBwFilterNum);
}
}
m_pQX_Merge_Analysis->setSendStatus(m_strcameraName, QX_Merge_Analysis::RUN_STATUS_READY);
m_pdetlog->AddCheckstr(PrintLevel_1, "ResultParamJudge", "===============QX_Merge_Analysis===start %s", CheckUtil::getCurTimeHMS().c_str());
@ -360,9 +381,10 @@ int CameraCheckAnalysisy::ResultParamJudge()
long t1 = CheckUtil::getcurTime();
int re = m_imageResultJudge.MergeResult(m_pCheck_Result->ImageALLDetResultList.at(imgidx), ptemre);
long t2 = CheckUtil::getcurTime();
#ifdef AI_Time
if (m_pbaseCheckFunction != NULL && m_pbaseCheckFunction->bDrawAndSave)
{
m_imageResultJudge.DrawResult(m_pCheck_Result->ImageALLDetResultList.at(imgidx));
#endif
}
long t3 = CheckUtil::getcurTime();
// m_pdetlog->AddCheckstr(PrintLevel_1, "ResultParamJudge", " det time %ld ms judge %ld draw %ld", t3 - t1, t2 - t1, t3 - t2);
@ -695,7 +717,13 @@ 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;
ChannelCheckFunction *pChannelFuntion_BQ = GetChannelFuntion(L255->strChannel);
if (pChannelFuntion_BQ)
{
m_pCheck_Result->cameraBaseResult->pBQ_Result->bBQ_AI_Det = pChannelFuntion_BQ->function.f_Det_BQ.bBQ_AI_Det;
m_pCheck_Result->cameraBaseResult->pBQ_Result->bsaveAll = pChannelFuntion_BQ->function.f_Det_BQ.bsaveAll;
}
cv::Rect Det_CropRoi = m_pCheck_Result->cameraBaseResult->pEdgeDetResult->cutRoi;
@ -716,25 +744,36 @@ int CameraCheckAnalysisy::preDet_BQ(const cv::Mat &L255CutImg, std::shared_ptr<I
return re;
}
// 如果要屏蔽标签
if (m_pCheck_Result->cameraBaseResult->pBQ_Result->bShield_BQ)
// 默认屏蔽标签
{
m_pdetlog->AddCheckstr(PrintLevel_2, "preDet_BQ", "shieldBQ is Open");
cv::Mat detImg_mask = m_pCheck_Result->cameraBaseResult->pEdgeDetResult->shieldMask;
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);
int BQ_expand = pChannelFuntion_BQ ? pChannelFuntion_BQ->function.f_Det_BQ.BQ_expand : 0;
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)
{
detImg_mask(boundingRect).setTo(255);
m_pCheck_Result->cameraBaseResult->pBQ_Result->pBQ_expandedRoiList.push_back(boundingRect);
// 保存裁剪图像供独立BQ AI检测
if (pChannelFuntion_BQ && pChannelFuntion_BQ->function.f_Det_BQ.bBQ_AI_Det)
{
cv::Mat bqCrop = L255CutImg(boundingRect).clone();
m_pCheck_Result->cameraBaseResult->pBQ_Result->pBQ_cropImages.push_back(bqCrop);
}
}
}
if (L255->result->in_shareImage->bDebugsaveImg)
{
cv::imwrite("sheildImg.png", m_pCheck_Result->cameraBaseResult->pEdgeDetResult->shieldMask);
}
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_2, "preDet_BQ", "shieldBQ is false");
}
std::shared_ptr<BQ_Result> pBQ_Result = m_pCheck_Result->cameraBaseResult->pBQ_Result; // BQ 检测结果
pBQ_Result->BQ_centerPoint.erase(pBQ_Result->BQ_centerPoint.begin(), pBQ_Result->BQ_centerPoint.end());
for (int i = 0; i < pBQ_Result->pBQ_roiList.size(); i++)
@ -1235,43 +1274,55 @@ 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);
char hour_buf[4];
strftime(hour_buf, sizeof(hour_buf), "%H", &tm_now);
std::string hour_str(hour_buf);
auto task = std::async(std::launch::async, [result_copy, save_path_root, date_str, hour_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;
// 检查是否为日期格式目录 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)) {
struct stat lst;
if (lstat(full_path.c_str(), &lst) == 0 && !S_ISLNK(lst.st_mode)) {
dirs.emplace_back(full_path, st.st_mtime);
}
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");
closedir(dir);
}
}
@ -1295,13 +1346,14 @@ int CameraCheckAnalysisy::ngImgSave() {
std::string safe_channel = SanitizePathComponent(raw_channel);
// 只保存NG结果且图像数据非空
if ((m_CheckResult_shareP->nresult == 1 || safe_channel == "L255") &&
!m_CheckResult_shareP->resultMaskImg.empty() &&
!m_CheckResult_shareP->resultimg.empty() &&
!m_CheckResult_shareP->cutSrcimg.empty()) {
// if ((m_CheckResult_shareP->nresult == 1 || safe_channel == "L255") &&
// !m_CheckResult_shareP->resultMaskImg.empty() &&
// !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 + "/" + hour_str + "/" + safe_product + "/" + safe_camera + "/";
if (CheckUtil::CreateDir(save_dir) != 0) {
cerr << ("Failed to create directory: " + save_dir);
continue;
@ -1316,7 +1368,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)
{
// 计算交集区域

@ -1,5 +1,7 @@
#include "ImageResultJudge.h"
#include <cmath>
#include <algorithm>
ImageResultJudge::ImageResultJudge(/* args */)
{
@ -96,6 +98,12 @@ cv::Rect ImageResultJudge::GetCutRoi(cv::Rect &roi, const cv::Mat &img)
return cutroi;
}
bool ImageResultJudge::NeedDrawAndSave()
{
// base 层“是否存图绘制”开关:绘制并保存结果图(运行时替代 SAVE_RESIMG 宏)
return m_pbaseCheckFunction != NULL && m_pbaseCheckFunction->bDrawAndSave;
}
int ImageResultJudge::GetAIDetImg(std::shared_ptr<ImageAllResult> pImageResult, cv::Point pcenter, cv::Mat &AI_InImg, cv::Mat &AI_OutImg)
{
@ -222,6 +230,25 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
QX_info->result = QX_RESULT_TYPE_NoJduge;
// 关联通道分析:登记关联过滤源缺陷位置(类型由 analysis_bind.correlated_types 配置, 如脏污/暗点),
// 供 AD 分析判断关联通道是否存在重叠缺陷。
// 登记只依赖缺陷类型与位置,与判定结果/是否绘制无关:
// 被分到 NoJudge(不判定/Draw)的缺陷也要登记(如 DP 上未开启 AD 判定的暗点),否则无法过滤其它通道的重叠缺陷
if (m_pQX_Merge_Analysis->IsCorrelatedSourceConfigType(config_qx_type))
{
QX_Merge_Analysis::Correlated_Qx_Info_ corr_qx;
corr_qx.channel_name = pImageResult->strChannel;
corr_qx.camera_name = pImageResult->cameraBaseResult->strCameraName;
corr_qx.config_qx_type = config_qx_type;
corr_qx.result_type = ConfigTypeToResultType(config_qx_type);
corr_qx.product_roi = roi;
corr_qx.product_roi.x += fproduct_offx_piexl;
bool bAddCorr = m_pQX_Merge_Analysis->AddCorrelatedQxInfo(corr_qx, pQxLog);
pQxLog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, " Use Num And Dis Judge ", "qx %s ,Add Correlated %s %s",
qx_name.c_str(), BOOL_TO_STR_Error(bAddCorr), corr_qx.GetInfo().c_str());
}
// 在其他位置已经判断了 不进入分析。直接绘制。
if (qx_status == QX_Stauts_Draw)
{
@ -327,6 +354,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 +413,31 @@ 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);
}
// AD根据配置检测:未配置通道的AD缺陷不判NG
if (config_qx_type == CONFIG_QX_NAME_AD && m_pbaseCheckFunction && ict == ANALYSIS_TYPE_TF &&
std::find(m_pbaseCheckFunction->ad_check.analysis_sf.region_sf.begin(),
m_pbaseCheckFunction->ad_check.analysis_sf.region_sf.end(),
strChannel) == m_pbaseCheckFunction->ad_check.analysis_sf.region_sf.end())
{
nerrortype = 0;
result = true;
bNG_Status = false;
bYS_Status = false;
pQxLog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "Param Judge", "AD channel %s not configured in region_sf", strChannel.c_str());
}
if (!result)
{
break;
@ -415,36 +468,37 @@ 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.
// if ((nerrortype == 0 || checkFlage != ANALYSIS_TYPE_TF) &&
// config_qx_type == CONFIG_QX_NAME_AD && pFuntion &&
// pFuntion->function.f_AD_Check.bOpen &&
// pFuntion->function.f_AD_Check.analysis_s.bOpen &&
// pFuntion->function.f_AD_Check.analysis_s.NG_3s && bpreSucc)
if ((nerrortype == 0 || checkFlage != ANALYSIS_TYPE_TF) &&
config_qx_type == CONFIG_QX_NAME_AD && m_pbaseCheckFunction &&
config_qx_type == CONFIG_QX_NAME_AD && pFuntion != NULL && m_pbaseCheckFunction &&
m_pbaseCheckFunction->ad_check.bOpen &&
m_pbaseCheckFunction->ad_check.analysis_s.bOpen &&
m_pbaseCheckFunction->ad_check.analysis_s.NG_3s && bpreSucc)
{
bool b3sChannel = false;
if (std::find(m_pbaseCheckFunction->ad_check.analysis_sf.s_sf.begin(), m_pbaseCheckFunction->ad_check.analysis_sf.s_sf.end(), strChannel) != m_pbaseCheckFunction->ad_check.analysis_sf.s_sf.end())
{
b3sChannel = true;
}
Judge_Status = true;
int det_s_Vale = 0;
if (JudgArea >= pFuntion->function.f_AD_Check.S_standard_3s.area &&
flen >= pFuntion->function.f_AD_Check.S_standard_3s.len)
if (JudgArea >= m_pbaseCheckFunction->ad_check.S_standard_3s.area &&
flen >= m_pbaseCheckFunction->ad_check.S_standard_3s.len && b3sChannel)
{
nerrortype = 1;
checkFlage = ANALYSIS_TYPE_TF;
bNG_Status = true;
pQxLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "info", " AD 3S result NG, det area %f len %f >=3s param area %f len %f ",
JudgArea, flen, pFuntion->function.f_AD_Check.S_standard_3s.area, pFuntion->function.f_AD_Check.S_standard_3s.len);
JudgArea, flen, m_pbaseCheckFunction->ad_check.S_standard_3s.area, m_pbaseCheckFunction->ad_check.S_standard_3s.len);
}
else
{
pQxLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "info", " AD 3S result OK, det area %f len %f < 3s param area %f len %f ",
JudgArea, flen, pFuntion->function.f_AD_Check.S_standard_3s.area, pFuntion->function.f_AD_Check.S_standard_3s.len);
JudgArea, flen, m_pbaseCheckFunction->ad_check.S_standard_3s.area, m_pbaseCheckFunction->ad_check.S_standard_3s.len);
}
}
@ -479,7 +533,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
{
// 目前为止判断是好品
if (!bNG_Status && pFuntion->function.f_Big_QX.bOpen)
if (!bNG_Status && pFuntion != NULL && pFuntion->function.f_Big_QX.bOpen)
{
Judge_Status = true;
bool berror = false;
@ -503,7 +557,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
}
// 总面积判断
if (!bNG_Status && pFuntion->function.f_Big_QX.bOpen)
if (!bNG_Status && pFuntion != NULL && pFuntion->function.f_Big_QX.bOpen)
{
if (nSumBLobNUm <= pFuntion->function.f_Big_QX.Sum_blob_Num)
{
@ -550,6 +604,17 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
pQxLog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, " big qx ", "qx %s ,result = %s", qx_name.c_str(), resultType.c_str());
}
// 对于 AD_YX 类型,面积小于设定值的 降级为NoJudge
if (config_qx_type == CONFIG_QX_NAME_AD_YX && bYS_Status && pFuntion != NULL &&
pFuntion->function.f_YXDet.bOpen && JudgArea < pFuntion->function.f_YXDet.minArea)
{
bYS_Status = false;
QX_info->result = QX_RESULT_TYPE_OK;
QX_info->result_name = QX_RESULT_TYPE_Names[QX_info->result];
resultType = QX_info->result_name;
pQxLog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "AD_YX downgrade", "area %f < %d, YS -> OK", JudgArea, pFuntion->function.f_YXDet.minArea);
}
if (bNG_Status)
{
m_CheckResult_shareP->nresult = 1;
@ -571,7 +636,8 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
// CheckUtil::printROI(CutRoi, "CutRoi");
cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT);
#ifdef AI_Time
if (NeedDrawAndSave())
{
if (pImageResult->detImg.channels() == 1)
{
cv::cvtColor(pImageResult->detImg(CutRoi), tem.srcImg, cv::COLOR_GRAY2BGR);
@ -581,7 +647,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
tem.srcImg = pImageResult->detImg(CutRoi).clone();
}
cv::resize(tem.srcImg, tem.resizeImg, sz);
#endif
}
int nqx_type = ConfigTypeToResultType(config_qx_type);
tem.type = nqx_type;
@ -626,7 +692,10 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
m_CheckResult_shareP->nresult = nqx_type;
}
if (NeedDrawAndSave())
{
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
}
m_CheckResult_shareP->qxImageResult.push_back(tem);
}
else
@ -637,13 +706,15 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
m_CheckResult_shareP->nYS_result = nqx_type;
}
if (NeedDrawAndSave())
{
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
}
m_CheckResult_shareP->YS_ImageResult.push_back(tem);
}
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, " result ", " name %s ---code %s", tem.strTypeName.c_str(), tem.qx_Code.c_str());
}
// 对于大面积 的zara和 异显NG ,可以不接着分析后续的数据了。应为后续估计有很多错误类别。
@ -747,6 +818,573 @@ int ImageResultJudge::UpdateMergedet(std::shared_ptr<CameraResult> pCheck_Result
return 0;
}
/**
* @brief 关联过滤源过滤:撤销与关联通道过滤源缺陷重叠的暗点NG结果(含单图判定的单点NG)
* 被过滤的暗点不再计入NG
* @param pImageResult 当前检测图片结果
* @return int 被过滤的缺陷个数
*/
int ImageResultJudge::FilterCorrelatedDirtyNG(std::shared_ptr<ImageAllResult> pImageResult)
{
if (pImageResult == nullptr || pImageResult->cameraBaseResult == nullptr ||
m_pbaseCheckFunction == nullptr || m_pQX_Merge_Analysis == nullptr || m_pAnalysisyConfig == nullptr)
{
return 0;
}
// 未配置关联通道(NONE/空)时不需要判断
std::vector<std::string> correlated_channels = m_pbaseCheckFunction->ad_check.analysis_bind.correlated_channels;
if (correlated_channels.empty())
{
return 0;
}
std::shared_ptr<One_Image_CheckResult_> pDetResult = pImageResult->pDetResult; // 检测结果
std::shared_ptr<CheckResult> m_CheckResult_shareP = pImageResult->result; // 返回结果
if (pDetResult == nullptr || pDetResult->pQx_ErrorList == nullptr || m_CheckResult_shareP == nullptr)
{
return 0;
}
std::shared_ptr<DetLog> pImageLog = pImageResult->detlog;
float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm;
int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X);
int nFilterNum = 0;
for (int qxidx = 0; qxidx < (int)pDetResult->pQx_ErrorList->size(); qxidx++)
{
QX_ERROR_INFO_ *QX_info = &pDetResult->pQx_ErrorList->at(qxidx);
// 只处理 暗点(AD) 的 NG 结果
if (QX_info->nconfig_qx_type != CONFIG_QX_NAME_AD || QX_info->result != QX_RESULT_TYPE_NG)
{
continue;
}
// 关联通道存在与当前缺陷重叠的过滤源缺陷时,过滤该暗点NG
cv::Rect product_roi = QX_info->roi;
product_roi.x += fproduct_offx_piexl;
if (!m_pQX_Merge_Analysis->IsDirtyOverlapInCorrelatedChannel(pImageResult->strChannel,
pImageResult->cameraBaseResult->strCameraName,
product_roi,
pImageLog))
{
continue;
}
// 撤销NG结果
UndoNGResult(pImageResult, qxidx, QX_info);
nFilterNum++;
if (pImageLog)
{
pImageLog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "DirtyCorrelatedFilter",
"img %s cam %s blob %d qx %s roi %s : 关联通道存在重叠的过滤源缺陷, 过滤该暗点NG",
pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(),
qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str());
}
if (m_pQX_Merge_Analysis->m_pMergedetlog)
{
m_pQX_Merge_Analysis->m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, "DirtyCorrelatedFilter",
"img %s cam %s blob %d qx %s roi %s : 关联通道存在重叠的过滤源缺陷, 过滤该暗点NG",
pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(),
qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str());
}
}
return nFilterNum;
}
/**
* @brief 撤销单个缺陷的NG结果:恢复为OK,移除对应的NG小图并回退计数
* @param pImageResult 当前检测图片结果
* @param qxidx 缺陷在 pQx_ErrorList 中的下标
* @param QX_info 缺陷信息
* @return int 移除的NG小图个数
*/
int ImageResultJudge::UndoNGResult(std::shared_ptr<ImageAllResult> pImageResult, int qxidx, QX_ERROR_INFO_ *QX_info)
{
if (pImageResult == nullptr || QX_info == nullptr)
{
return 0;
}
std::shared_ptr<CheckResult> m_CheckResult_shareP = pImageResult->result;
if (m_CheckResult_shareP == nullptr)
{
return 0;
}
// 撤销NG结果
QX_info->result = QX_RESULT_TYPE_OK;
QX_info->result_name = QX_RESULT_TYPE_Names[QX_info->result];
int nqx_type = ConfigTypeToResultType(QX_info->nconfig_qx_type);
// 移除该缺陷的NG小图
int nEraseNum = 0;
for (auto it = m_CheckResult_shareP->qxImageResult.begin(); it != m_CheckResult_shareP->qxImageResult.end();)
{
if (it->idx == qxidx)
{
it = m_CheckResult_shareP->qxImageResult.erase(it);
nEraseNum++;
}
else
{
++it;
}
}
// 计数回退
if (nqx_type >= 0 && nqx_type < ERROR_TYPE_COUNT)
{
m_CheckResult_shareP->defectResultList[nqx_type].num -= nEraseNum;
if (m_CheckResult_shareP->defectResultList[nqx_type].num < 0)
{
m_CheckResult_shareP->defectResultList[nqx_type].num = 0;
}
}
if (pImageResult->NG_num > 0)
{
pImageResult->NG_num--;
}
pImageResult->Ok_num++;
// 过滤后已无NG,则恢复图片结果
if (m_CheckResult_shareP->qxImageResult.empty())
{
m_CheckResult_shareP->nresult = ERROR_TYPE_OK;
}
return nEraseNum;
}
/**
* @brief 判断盲失(产品坐标系ROI)在配置的过滤通道是否存在同位置缺陷(不限制缺陷类型)
* 同位置判断方式参考暗点联合分析:当前ROI与过滤通道缺陷ROI双方均按 expand 扩张后计算 IOU
* @param imgResults 当前相机所有通道的检测结果
* @param cur_channel_name 盲失所属通道(主通道)
* @param qx_product_roi 盲失的产品坐标系ROI
* @param roi_expand 扩张像素(ms_expand)
* @param iou_threshold IOU阈值(ms_iou)
* @param plog 日志
* @return bool 过滤通道同位置存在缺陷返回true
*/
bool ImageResultJudge::IsOverlapQxInMsFilterChannel(const std::vector<std::shared_ptr<ImageAllResult>> &imgResults,
const std::string &cur_channel_name,
const cv::Rect &qx_product_roi,
int roi_expand, float iou_threshold,
std::shared_ptr<DetLog> plog)
{
// 防御:ROI非法时不判断,避免无效计算与除零
if (qx_product_roi.width <= 0 || qx_product_roi.height <= 0)
{
return false;
}
if (m_pbaseCheckFunction == nullptr || m_pAnalysisyConfig == nullptr)
{
return false;
}
const std::vector<std::string> &filter_channels = m_pbaseCheckFunction->ms_check.ms_filter_channels;
if (filter_channels.empty())
{
return false;
}
float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm;
int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X);
// 与暗点联合分析保持一致:双方均按 expand 扩张后再计算 IOU
cv::Rect roi_expanded(qx_product_roi.x - roi_expand, qx_product_roi.y - roi_expand,
qx_product_roi.width + roi_expand * 2, qx_product_roi.height + roi_expand * 2);
for (int imgidx = 0; imgidx < (int)imgResults.size(); imgidx++)
{
std::shared_ptr<ImageAllResult> pFilterImage = imgResults.at(imgidx);
if (pFilterImage == nullptr)
{
continue;
}
// 只与配置的过滤通道比较;当前通道不参与,避免自身重叠导致误过滤
if (pFilterImage->strChannel == cur_channel_name)
{
continue;
}
if (std::find(filter_channels.begin(), filter_channels.end(), pFilterImage->strChannel) == filter_channels.end())
{
continue;
}
std::shared_ptr<One_Image_CheckResult_> pFilterDetResult = pFilterImage->pDetResult;
if (pFilterDetResult == nullptr || pFilterDetResult->pQx_ErrorList == nullptr)
{
continue;
}
// 过滤通道不限制缺陷类型:任意类型缺陷都参与同位置比较,与具体判定结果无关
for (int j = 0; j < (int)pFilterDetResult->pQx_ErrorList->size(); j++)
{
const QX_ERROR_INFO_ &filter_qx = pFilterDetResult->pQx_ErrorList->at(j);
cv::Rect filter_roi = filter_qx.roi;
filter_roi.x += fproduct_offx_piexl;
cv::Rect filter_expanded(filter_roi.x - roi_expand, filter_roi.y - roi_expand,
filter_roi.width + roi_expand * 2, filter_roi.height + roi_expand * 2);
float fiou = CheckUtil::CalIoU(roi_expanded, filter_expanded);
if (plog)
{
plog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "MsBlind",
"main channel %s roi %s <-> filter channel %s qx %d roi %s expand %d iou %f",
cur_channel_name.c_str(), CheckUtil::GetRectString(qx_product_roi).c_str(),
pFilterImage->strChannel.c_str(), j,
CheckUtil::GetRectString(filter_roi).c_str(), roi_expand, fiou);
}
// IOU 为 NaN(ROI 非法导致的除零)时比较结果为 false,不会误过滤
if (fiou > iou_threshold)
{
if (plog)
{
plog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "MsBlind",
"main channel %s roi %s overlap filter channel %s qx %d roi %s expand %d iou %f > %f",
cur_channel_name.c_str(), CheckUtil::GetRectString(qx_product_roi).c_str(),
pFilterImage->strChannel.c_str(), j,
CheckUtil::GetRectString(filter_roi).c_str(), roi_expand, fiou, iou_threshold);
}
return true;
}
}
}
return false;
}
/**
* @brief 盲失联合判断:主通道(ms_main_channel)检出的盲失(Other)需要和过滤通道(ms_filter_channels)同位置判断,
* 过滤通道同位置存在缺陷(不限制缺陷类型)时不算盲失(撤销NG)
* 同位置判断方式参考暗点联合分析:双方按 ms_expand 扩张后计算 IOU, 大于 ms_iou 认为同位置
* @param pCheck_Result 当前相机检测结果(包含该相机所有通道的检测结果)
* @return int 被过滤的盲失NG个数
*/
int ImageResultJudge::FilterMsBlindNG(std::shared_ptr<CameraResult> pCheck_Result)
{
if (pCheck_Result == nullptr || m_pbaseCheckFunction == nullptr || m_pAnalysisyConfig == nullptr)
{
return 0;
}
Base_Function_MS_Check *pMsParam = &m_pbaseCheckFunction->ms_check;
// 未开启盲失分析 或 未配置主通道/过滤通道 时不需要判断
if (!pMsParam->bOpen || pMsParam->ms_main_channel.empty() || pMsParam->ms_filter_channels.empty())
{
return 0;
}
const int roi_expand = (int)(round)(pMsParam->MS_Expand);
const float iou_threshold = pMsParam->MS_IOU;
float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm;
int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X);
int nFilterNum = 0;
for (int imgidx = 0; imgidx < (int)pCheck_Result->ImageALLDetResultList.size(); imgidx++)
{
std::shared_ptr<ImageAllResult> pImageResult = pCheck_Result->ImageALLDetResultList.at(imgidx);
if (pImageResult == nullptr)
{
continue;
}
// 只处理配置的主通道
if (std::find(pMsParam->ms_main_channel.begin(), pMsParam->ms_main_channel.end(), pImageResult->strChannel) == pMsParam->ms_main_channel.end())
{
continue;
}
std::shared_ptr<One_Image_CheckResult_> pDetResult = pImageResult->pDetResult; // 检测结果
if (pDetResult == nullptr || pDetResult->pQx_ErrorList == nullptr || pImageResult->result == nullptr)
{
continue;
}
std::shared_ptr<DetLog> pImageLog = pImageResult->detlog;
for (int qxidx = 0; qxidx < (int)pDetResult->pQx_ErrorList->size(); qxidx++)
{
QX_ERROR_INFO_ *QX_info = &pDetResult->pQx_ErrorList->at(qxidx);
// 只处理 盲失(Other) 的 NG 结果
if (QX_info->nconfig_qx_type != CONFIG_QX_NAME_Other || QX_info->result != QX_RESULT_TYPE_NG)
{
continue;
}
cv::Rect product_roi = QX_info->roi;
product_roi.x += fproduct_offx_piexl;
// 过滤通道同位置不存在盲失时,保留盲失NG
if (!IsOverlapQxInMsFilterChannel(pCheck_Result->ImageALLDetResultList,
pImageResult->strChannel,
product_roi,
roi_expand, iou_threshold,
pImageLog))
{
continue;
}
// 撤销NG结果
UndoNGResult(pImageResult, qxidx, QX_info);
nFilterNum++;
if (pImageLog)
{
pImageLog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "MsBlind",
"img %s cam %s blob %d qx %s roi %s : 过滤通道同位置存在缺陷, 不算盲失",
pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(),
qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str());
}
if (m_pQX_Merge_Analysis != nullptr && m_pQX_Merge_Analysis->m_pMergedetlog)
{
m_pQX_Merge_Analysis->m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, "MsBlind",
"img %s cam %s blob %d qx %s roi %s : 过滤通道同位置存在缺陷, 不算盲失",
pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(),
qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str());
}
}
}
return nFilterNum;
}
/**
* @brief 跨通道同位置判断:判断当前缺陷(产品坐标系ROI)在过滤通道是否存在同位置缺陷
* (match_qx_types 为空表示不限制缺陷类型)
* 同位置判断方式参考暗点联合分析:当前ROI与过滤通道缺陷ROI双方均按 expand 扩张后计算 IOU
* @param imgResults 当前相机所有通道的检测结果
* @param cur_channel_name 当前缺陷所属通道(主通道)
* @param filter_channels 需要比较的过滤通道
* @param match_qx_types 过滤通道中参与比较的缺陷类型(CONFIG_QX_NAME_),为空表示不限制类型
* @param qx_product_roi 当前缺陷的产品坐标系ROI
* @param roi_expand 扩张像素
* @param iou_threshold IOU阈值
* @param strTag 日志标签
* @param plog 日志
* @return bool 过滤通道同位置存在匹配缺陷返回true
*/
bool ImageResultJudge::IsOverlapQxInFilterChannels(const std::vector<std::shared_ptr<ImageAllResult>> &imgResults,
const std::string &cur_channel_name,
const std::vector<std::string> &filter_channels,
const std::vector<int> &match_qx_types,
const cv::Rect &qx_product_roi,
int roi_expand, float iou_threshold,
const std::string &strTag,
std::shared_ptr<DetLog> plog)
{
// 防御:ROI非法/未配置过滤通道时不判断,避免无效计算与除零
if (qx_product_roi.width <= 0 || qx_product_roi.height <= 0)
{
return false;
}
// match_qx_types 为空表示不限制过滤通道的缺陷类型
if (filter_channels.empty())
{
return false;
}
if (m_pAnalysisyConfig == nullptr)
{
return false;
}
float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm;
int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X);
// 与暗点联合分析保持一致:双方均按 expand 扩张后再计算 IOU
cv::Rect roi_expanded(qx_product_roi.x - roi_expand, qx_product_roi.y - roi_expand,
qx_product_roi.width + roi_expand * 2, qx_product_roi.height + roi_expand * 2);
for (int imgidx = 0; imgidx < (int)imgResults.size(); imgidx++)
{
std::shared_ptr<ImageAllResult> pFilterImage = imgResults.at(imgidx);
if (pFilterImage == nullptr)
{
continue;
}
// 只与配置的过滤通道比较;当前通道不参与,避免自身重叠导致误过滤
if (pFilterImage->strChannel == cur_channel_name)
{
continue;
}
if (std::find(filter_channels.begin(), filter_channels.end(), pFilterImage->strChannel) == filter_channels.end())
{
continue;
}
std::shared_ptr<One_Image_CheckResult_> pFilterDetResult = pFilterImage->pDetResult;
if (pFilterDetResult == nullptr || pFilterDetResult->pQx_ErrorList == nullptr)
{
continue;
}
// 只统计过滤通道内匹配类型的缺陷(未指定类型时不限制),与具体判定结果无关
for (int j = 0; j < (int)pFilterDetResult->pQx_ErrorList->size(); j++)
{
const QX_ERROR_INFO_ &filter_qx = pFilterDetResult->pQx_ErrorList->at(j);
if (!match_qx_types.empty() &&
std::find(match_qx_types.begin(), match_qx_types.end(), filter_qx.nconfig_qx_type) == match_qx_types.end())
{
continue;
}
cv::Rect filter_roi = filter_qx.roi;
filter_roi.x += fproduct_offx_piexl;
cv::Rect filter_expanded(filter_roi.x - roi_expand, filter_roi.y - roi_expand,
filter_roi.width + roi_expand * 2, filter_roi.height + roi_expand * 2);
float fiou = CheckUtil::CalIoU(roi_expanded, filter_expanded);
if (plog)
{
plog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, strTag,
"main channel %s roi %s <-> filter channel %s qx %d roi %s expand %d iou %f",
cur_channel_name.c_str(), CheckUtil::GetRectString(qx_product_roi).c_str(),
pFilterImage->strChannel.c_str(), j,
CheckUtil::GetRectString(filter_roi).c_str(), roi_expand, fiou);
}
// IOU 为 NaN(ROI 非法导致的除零)时比较结果为 false,不会误过滤
if (fiou > iou_threshold)
{
if (plog)
{
plog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strTag,
"main channel %s roi %s overlap filter channel %s qx %d roi %s expand %d iou %f > %f",
cur_channel_name.c_str(), CheckUtil::GetRectString(qx_product_roi).c_str(),
pFilterImage->strChannel.c_str(), j,
CheckUtil::GetRectString(filter_roi).c_str(), roi_expand, fiou, iou_threshold);
}
return true;
}
}
}
return false;
}
/**
* @brief 跨通道联合判断(通用):主通道中指定类型的NG缺陷,若在过滤通道同位置存在指定类型的缺陷,则撤销该NG
* 同位置判断方式参考暗点联合分析:双方按 expand 扩张后计算 IOU, 大于 iou_threshold 认为同位置
* @param pCheck_Result 当前相机检测结果(包含该相机所有通道的检测结果)
* @param main_channels 主通道
* @param filter_channels 过滤通道
* @param main_qx_types 参与判断的主通道缺陷类型(CONFIG_QX_NAME_)
* @param match_qx_types 过滤通道中参与比较的缺陷类型(CONFIG_QX_NAME_),为空表示不限制类型
* @param roi_expand 扩张像素
* @param iou_threshold IOU阈值
* @param strTag 日志标签
* @param strReason 过滤原因(日志)
* @return int 被过滤的NG个数
*/
int ImageResultJudge::FilterCrossChannelNG(std::shared_ptr<CameraResult> pCheck_Result,
const std::vector<std::string> &main_channels,
const std::vector<std::string> &filter_channels,
const std::vector<int> &main_qx_types,
const std::vector<int> &match_qx_types,
int roi_expand, float iou_threshold,
const std::string &strTag,
const std::string &strReason)
{
if (pCheck_Result == nullptr || m_pAnalysisyConfig == nullptr)
{
return 0;
}
// 未配置主通道/过滤通道/判断类型时不需要判断
if (main_channels.empty() || filter_channels.empty() || main_qx_types.empty())
{
return 0;
}
float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm;
int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X);
int nFilterNum = 0;
for (int imgidx = 0; imgidx < (int)pCheck_Result->ImageALLDetResultList.size(); imgidx++)
{
std::shared_ptr<ImageAllResult> pImageResult = pCheck_Result->ImageALLDetResultList.at(imgidx);
if (pImageResult == nullptr)
{
continue;
}
// 只处理配置的主通道
if (std::find(main_channels.begin(), main_channels.end(), pImageResult->strChannel) == main_channels.end())
{
continue;
}
std::shared_ptr<One_Image_CheckResult_> pDetResult = pImageResult->pDetResult; // 检测结果
if (pDetResult == nullptr || pDetResult->pQx_ErrorList == nullptr || pImageResult->result == nullptr)
{
continue;
}
std::shared_ptr<DetLog> pImageLog = pImageResult->detlog;
for (int qxidx = 0; qxidx < (int)pDetResult->pQx_ErrorList->size(); qxidx++)
{
QX_ERROR_INFO_ *QX_info = &pDetResult->pQx_ErrorList->at(qxidx);
// 只处理指定类型的 NG 结果(YS 不降级)
if (QX_info->result != QX_RESULT_TYPE_NG)
{
continue;
}
if (std::find(main_qx_types.begin(), main_qx_types.end(), QX_info->nconfig_qx_type) == main_qx_types.end())
{
continue;
}
cv::Rect product_roi = QX_info->roi;
product_roi.x += fproduct_offx_piexl;
// 过滤通道同位置不存在匹配类型的缺陷时,保留该NG
if (!IsOverlapQxInFilterChannels(pCheck_Result->ImageALLDetResultList,
pImageResult->strChannel,
filter_channels, match_qx_types,
product_roi,
roi_expand, iou_threshold,
strTag, pImageLog))
{
continue;
}
// 撤销NG结果
UndoNGResult(pImageResult, qxidx, QX_info);
nFilterNum++;
if (pImageLog)
{
pImageLog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strTag,
"img %s cam %s blob %d qx %s roi %s : %s",
pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(),
qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str(),
strReason.c_str());
}
if (m_pQX_Merge_Analysis != nullptr && m_pQX_Merge_Analysis->m_pMergedetlog)
{
m_pQX_Merge_Analysis->m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, strTag,
"img %s cam %s blob %d qx %s roi %s : %s",
pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(),
qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str(),
strReason.c_str());
}
}
}
return nFilterNum;
}
/**
* @brief cell黑白点联合判断:主通道(cellbw_main_channel)检出的cell黑白点(127画面的 Cell127 也算白点)
* 在过滤通道(cellbw_filter_channels)同位置存在缺陷(不限制缺陷类型)时不算cell黑白点(撤销NG)
* 同位置判断方式参考暗点联合分析:双方按 cellbw_expand 扩张后计算 IOU, 大于 cellbw_iou 认为同位置
* @param pCheck_Result 当前相机检测结果(包含该相机所有通道的检测结果)
* @return int 被过滤的cell黑白点NG个数
*/
int ImageResultJudge::FilterCellBwNG(std::shared_ptr<CameraResult> pCheck_Result)
{
if (m_pbaseCheckFunction == nullptr)
{
return 0;
}
Base_Function_CellBW_Check *pCellBwParam = &m_pbaseCheckFunction->cellbw_check;
// 未开启cell黑白点分析时不需要判断(主通道/过滤通道为空时由通用逻辑直接返回)
if (!pCellBwParam->bOpen)
{
return 0;
}
// 主通道只处理 cell 黑白点(白点/黑点都算,Cell127 按白点处理);过滤通道不限制缺陷类型,同位置存在任意类型缺陷即过滤
const std::vector<int> qxTypes = {CONFIG_QX_NAME_white_Cell, CONFIG_QX_NAME_black_Cell, CONFIG_QX_NAME_127Cell};
return FilterCrossChannelNG(pCheck_Result,
pCellBwParam->cellbw_main_channel, pCellBwParam->cellbw_filter_channels,
qxTypes, std::vector<int>(),
(int)(round)(pCellBwParam->CellBW_Expand), pCellBwParam->CellBW_IOU,
"CellBw", "过滤通道同位置存在缺陷, 不算cell黑白点");
}
int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult, QX_Analysis_Result_List *ptemre)
{
@ -769,6 +1407,9 @@ int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult,
}
int qxNum = pDetResult->pQx_ErrorList->size();
// 关联过滤源过滤:撤销与关联通道过滤源缺陷重叠的暗点NG(含单图判定的单点NG)
FilterCorrelatedDirtyNG(pImageResult);
for (int iqx = 0; iqx < (int)ptemre->resultList.size(); iqx++)
{
if (ptemre->resultList.at(iqx).channel_name != pImageResult->strChannel ||
@ -814,7 +1455,8 @@ int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult,
cv::Rect CutRoi = GetCutRoi(roi, pImageResult->detImg);
cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT);
#ifdef AI_Time
if (NeedDrawAndSave())
{
if (pImageResult->detImg.channels() == 1)
{
cv::cvtColor(pImageResult->detImg(CutRoi), tem.srcImg, cv::COLOR_GRAY2BGR);
@ -824,7 +1466,7 @@ int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult,
tem.srcImg = pImageResult->detImg(CutRoi).clone();
}
cv::resize(tem.srcImg, tem.resizeImg, sz);
#endif
}
int nerrortype = 1;
int checkFlage = ANALYSIS_TYPE_TF;
@ -885,7 +1527,10 @@ int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult,
{
m_CheckResult_shareP->nresult = nqx_type;
}
if (NeedDrawAndSave())
{
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
}
m_CheckResult_shareP->qxImageResult.push_back(tem);
}
}
@ -1257,14 +1902,14 @@ int ImageResultJudge::ConfigTypeToResultType(int nconfigType)
{
bool bdesc = false;
int resultError_type = ERROR_TYPE_OK;
if(nconfigType < CONFIG_QX_NAME_Names.size())
if (nconfigType < CONFIG_QX_NAME_Names.size())
{
string cqn = CONFIG_QX_NAME_Names[nconfigType];
for(int i = 0; i < m_FlawCodeList.size(); i++)
for (int i = 0; i < m_FlawCodeList.size(); i++)
{
for(int j = 0; j < m_FlawCodeList.at(i).config_flaw_name.size(); j++)
for (int j = 0; j < m_FlawCodeList.at(i).config_flaw_name.size(); j++)
{
if(cqn == m_FlawCodeList.at(i).config_flaw_name[j])
if (cqn == m_FlawCodeList.at(i).config_flaw_name[j])
{
resultError_type = i;
bdesc = true;
@ -1274,7 +1919,7 @@ int ImageResultJudge::ConfigTypeToResultType(int nconfigType)
}
}
// 如果描述没有配置,用默认值
if(!bdesc)
if (!bdesc)
{
switch (nconfigType)
{
@ -1359,6 +2004,12 @@ int ImageResultJudge::ConfigTypeToResultType(int nconfigType)
case CONFIG_QX_NAME_LackPOL:
resultError_type = ERROR_TYPE_LackPol;
break;
case CONFIG_QX_NAME_RLine_X:
resultError_type = ERROR_TYPE_Line_X;
break;
case CONFIG_QX_NAME_RLine_Y:
resultError_type = ERROR_TYPE_Line_Y;
break;
default:
break;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -71,6 +71,8 @@ enum CONFIG_QX_NAME_
CONFIG_QX_NAME_black_Cell, // 白cell
CONFIG_QX_NAME_LD_HS, // 黑闪类画面的亮点
CONFIG_QX_NAME_LackPOL, // 缺失pol检测
CONFIG_QX_NAME_RLine_X, // 弱线 X
CONFIG_QX_NAME_RLine_Y, // 弱线 Y
CONFIG_QX_NAME_count,
};
// 缺陷项对应在参数中的名称
@ -105,7 +107,9 @@ static std::vector<std::string> CONFIG_QX_NAME_Names =
"white_Cell",
"black_Cell",
"LD_HS",
"LackPOL"};
"LackPOL",
"RLine_X",
"RLine_Y"};
// web控制检测的缺陷类型,
enum Function_CONFIG_QX_NAME_
@ -124,7 +128,7 @@ enum Function_CONFIG_QX_NAME_
Function_CONFIG_QX_NAME_PS, // ps(P0002)
Function_CONFIG_QX_NAME_Weak_Bright_Mura, // 白GAP(P1654)
Function_CONFIG_QX_NAME_No_Label, // 缺POL(P2833)
Function_CONFIG_QX_NAME_Other, // 缺POL(P2833)
Function_CONFIG_QX_NAME_Other, // 盲失
Function_CONFIG_QX_NAME_Chess, // Chess 异常
Function_CONFIG_QX_NAME_127Cell, // 白cell
Function_CONFIG_QX_NAME_Cell, // 独立检测的黑白cell点
@ -205,8 +209,7 @@ static const std::string IMG_CHANNEL_NAME[] =
"RGB-HGRAY",
"SCANR",
"SCANG",
"SCANB"
};
"SCANB"};
// 分析类型
enum ANALYSIS_TYPE_
{
@ -466,6 +469,7 @@ struct BasicConfig
height_max = 999999;
bDrawShieldRoi = false;
bShield_ZF = false;
bShield_BQ = true;
bDrawPreRoi = false;
fUP_IOU = 0.9;
bCal_ImageScale = false;
@ -794,6 +798,7 @@ struct Function_YXDet
{
bool bOpen; // 是否开启
std::string strModle;
int minArea;
Function_YXDet()
{
Init();
@ -802,20 +807,22 @@ struct Function_YXDet
{
bOpen = false;
strModle = "";
minArea = 0;
}
void copy(Function_YXDet tem)
{
this->bOpen = tem.bOpen;
this->strModle = tem.strModle;
this->minArea = tem.minArea;
}
void print(std::string str)
{
// printf("%s>>bOpen %d strModle %s\n", str.c_str(), bOpen, strModle.c_str());
// printf("%s>>bOpen %d strModle %s minArea %d\n", str.c_str(), bOpen, strModle.c_str(), minArea);
}
std::string GetInfo(std::string str)
{
char buffer[64];
sprintf(buffer, "%s>>bOpen %d strModle %s\n", str.c_str(), bOpen, strModle.c_str());
sprintf(buffer, "%s>>bOpen %d strModle %s minArea %d\n", str.c_str(), bOpen, strModle.c_str(), minArea);
std::string str123 = buffer;
return str123;
}
@ -869,6 +876,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 +893,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 +906,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)
{
@ -1271,7 +1294,19 @@ struct Function_Image_Align
{
printf("pointArry1 == 0 \n\n\n");
}
feature_Mask = tem_feature_Mask(feature_Roi).clone();
// 防御:feature_Roi 相对图像尺寸 img_W x img_H 可能越界,
// 越界会导致 cv::Mat(Mat, Rect) 断言失败并抛 cv::Exception,进而 abort。
cv::Rect roi = feature_Roi & cv::Rect(0, 0, img_W, img_H);
if (roi.width <= 0 || roi.height <= 0)
{
printf("[ToMaskImg] WARNING feature_Roi (%d,%d,%d,%d) out of range, img %dx%d\n",
feature_Roi.x, feature_Roi.y, feature_Roi.width, feature_Roi.height, img_W, img_H);
feature_Mask = tem_feature_Mask.clone();
}
else
{
feature_Mask = tem_feature_Mask(roi).clone();
}
}
}
void print(std::string str)
@ -1519,69 +1554,113 @@ struct AD_Analysisy_S
return str123;
}
};
// 暗点检测功能
struct Function_AD_Check
// 暗点同位置判断(IOU)
struct AD_Analysisy_IOU
{
bool bOpen; // 是否开启
AD_S_Standard S_standard_3s; // 3s 标准
AD_S_Standard S_standard_2s; // 2s 标准
AD_S_Standard S_standard_1s; // 1s 标准
AD_Analysisy_Num analysis_num; // 数量分析
AD_Analysisy_Dis analysis_dis; // 距离分析
AD_Analysisy_S analysis_s; // s 标准分析
Function_AD_Check()
float AD_IOU; // IOU 阈值
float AD_Expand; // 扩张阈值
AD_Analysisy_IOU()
{
Init();
}
void Init()
{
bOpen = false;
S_standard_3s.Init();
S_standard_2s.Init();
S_standard_1s.Init();
analysis_num.Init();
analysis_dis.Init();
analysis_s.Init();
AD_IOU = 0.15f;
AD_Expand = 0.0f;
}
void copy(Function_AD_Check tem)
void copy(AD_Analysisy_IOU tem)
{
this->bOpen = tem.bOpen;
this->S_standard_3s.copy(tem.S_standard_3s);
this->S_standard_2s.copy(tem.S_standard_2s);
this->S_standard_1s.copy(tem.S_standard_1s);
this->analysis_num.copy(tem.analysis_num);
this->analysis_dis.copy(tem.analysis_dis);
this->analysis_s.copy(tem.analysis_s);
this->AD_IOU = tem.AD_IOU;
this->AD_Expand = tem.AD_Expand;
}
void print(std::string str)
std::string GetInfo(std::string str)
{
printf("%s>>bOpen %d %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());
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 AD_Analysisy_Bind
{
bool bUse;
std::vector<std::string> main_channel_2s;
std::vector<std::string> second_channel_2s;
std::vector<std::string> main_channel_3s;
std::vector<std::string> second_channel_3s;
std::vector<std::string> correlated_channels;
// 关联过滤源缺陷类型名称(CONFIG_QX_NAME_Names 中的名称, 如 "Dirty_L0"/"Dirty_L1"/"Dirty_L2"/"AD")
// 仅这些类型的缺陷会被登记为过滤源(与 correlated_channels 配合使用); 为空时默认三种脏污(兼容旧配置)
std::vector<std::string> correlated_types;
AD_Analysisy_Bind()
{
Init();
}
void Init()
{
bUse = false;
main_channel_2s.clear();
second_channel_2s.clear();
main_channel_3s.clear();
second_channel_3s.clear();
correlated_channels.clear();
correlated_types.clear();
}
void copy(AD_Analysisy_Bind tem)
{
this->bUse = tem.bUse;
this->main_channel_2s = tem.main_channel_2s;
this->second_channel_2s = tem.second_channel_2s;
this->main_channel_3s = tem.main_channel_3s;
this->second_channel_3s = tem.second_channel_3s;
this->correlated_channels = tem.correlated_channels;
this->correlated_types = tem.correlated_types;
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%s>>bOpen %d ", str.c_str(), bOpen);
sprintf(buffer, "%s:bUse %d;", str.c_str(),
bUse);
std::string str123 = buffer;
str123 += S_standard_3s.GetInfo("3S");
str123 += S_standard_2s.GetInfo("2S");
str123 += S_standard_1s.GetInfo("1S");
str123 += analysis_num.GetInfo("analysis_num");
str123 += analysis_dis.GetInfo("analysis_dis");
str123 += analysis_s.GetInfo("analysis_s");
return str123;
}
};
struct AD_Analysisy_SF
{
std::vector<std::string> s_sf;
std::vector<std::string> region_sf;
AD_Analysisy_SF()
{
Init();
}
void Init()
{
s_sf.clear();
region_sf.clear();
}
void copy(AD_Analysisy_SF tem)
{
this->s_sf = tem.s_sf;
this->region_sf = tem.region_sf;
}
std::string GetInfo(std::string str)
{
std::string str123;
for (int i = 0; i < s_sf.size(); i++)
{
str123 += str + ":s_sf " + s_sf[i] + ";";
}
for (int i = 0; i < region_sf.size(); i++)
{
str123 += str + ":region_sf " + region_sf[i] + ";";
}
return str123;
}
};
// 异物检测功能
struct Function_POL_Check
{
@ -1670,6 +1749,74 @@ struct Function_Edge_Det
return str123;
}
};
struct Function_Det_BQ
{
bool bBQ_AI_Det; // 是否启用标签独立AI检测
int BQ_expand;
bool bsaveAll;
Function_Det_BQ()
{
Init();
}
void Init()
{
BQ_expand = 0;
bBQ_AI_Det = false;
bsaveAll = false;
}
void copy(Function_Det_BQ tem)
{
this->BQ_expand = tem.BQ_expand;
this->bBQ_AI_Det = tem.bBQ_AI_Det;
this->bsaveAll = tem.bsaveAll;
}
void print(std::string str)
{
printf("%d BQ_expand %d bBQ_AI_Det %d bsaveAll %d\n", str.c_str(),
BQ_expand, bBQ_AI_Det, bsaveAll);
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%d BQ_expand %d bBQ_AI_Det %d bsaveAll %d\n", str.c_str(),
BQ_expand, bBQ_AI_Det, bsaveAll);
std::string str123 = buffer;
return str123;
}
};
// 启用127cell 独立检测
struct Function_Det_RLine
{
bool bOpen; // 是否开启
bool bOpen_512; // 512模型是否开启。
Function_Det_RLine()
{
Init();
}
void Init()
{
bOpen = false;
bOpen_512 = false;
}
void copy(Function_Det_RLine tem)
{
this->bOpen = tem.bOpen;
this->bOpen_512 = tem.bOpen_512;
}
void print(std::string str)
{
printf("%s>>bOpen %d bOpen_512 %d \n", str.c_str(), bOpen, bOpen_512);
}
std::string GetInfo(std::string str)
{
char buffer[64];
sprintf(buffer, "%s>>bOpen %d bOpen_512 %d \n", str.c_str(), bOpen, bOpen_512);
std::string str123 = buffer;
return str123;
}
};
// 检测功能
struct CheckFunction
{
@ -1686,9 +1833,10 @@ struct CheckFunction
Function_Image_Align f_Image_Align; // 图片特征对齐
Function_Detect_LackPol f_Dectect_LackPol; // 缺失POL检测
Function_SecondDet f_SecondDetect; // 二次检测分析
Function_AD_Check f_AD_Check; // 暗点的检测功能
Function_POL_Check f_POL_Check; // 异物的检测功能
Function_Edge_Det f_dege_Det;
Function_Det_BQ f_Det_BQ;
Function_Det_RLine f_RLineDet; // 弱线检测参数
CheckFunction()
{
Init();
@ -1708,9 +1856,10 @@ struct CheckFunction
f_Image_Align.Init();
f_Dectect_LackPol.Init();
f_SecondDetect.Init();
f_AD_Check.Init();
f_POL_Check.Init();
f_dege_Det.Init();
f_Det_BQ.Init();
f_RLineDet.Init();
}
void copy(CheckFunction tem)
{
@ -1728,9 +1877,10 @@ struct CheckFunction
this->f_Image_Align.copy(tem.f_Image_Align);
this->f_Dectect_LackPol.copy(tem.f_Dectect_LackPol);
this->f_SecondDetect.copy(tem.f_SecondDetect);
this->f_AD_Check.copy(tem.f_AD_Check);
this->f_POL_Check.copy(tem.f_POL_Check);
this->f_dege_Det.copy(tem.f_dege_Det);
this->f_Det_BQ.copy(tem.f_Det_BQ);
this->f_RLineDet.copy(tem.f_RLineDet);
}
void print(std::string str)
{
@ -1748,9 +1898,10 @@ struct CheckFunction
f_Image_Align.print("Image_Align");
f_Dectect_LackPol.print("Dectect_LackPol");
f_SecondDetect.print("SecondDetect");
f_AD_Check.print("f_AD_Check");
f_POL_Check.print("f_POL_Check");
f_dege_Det.print("f_dege_Det");
f_Det_BQ.print("f_Det_BQ");
f_RLineDet.print("f_RLineDet");
}
std::string GetInfo(std::string str)
{
@ -1769,9 +1920,10 @@ struct CheckFunction
str123 += f_Image_Align.GetInfo("Image_Align");
str123 += f_Dectect_LackPol.GetInfo("Dectect_LackPol");
str123 += f_SecondDetect.GetInfo("SecondDetect");
str123 += f_AD_Check.GetInfo("f_AD_Check");
str123 += f_POL_Check.GetInfo("f_POL_Check");
str123 += f_dege_Det.GetInfo("f_dege_Det");
str123 += f_Det_BQ.GetInfo("f_Det_BQ");
str123 += f_RLineDet.GetInfo("f_RLineDet");
return str123;
}
};
@ -2155,6 +2307,9 @@ 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 分析
AD_Analysisy_Bind analysis_bind; // 绑定分析
AD_Analysisy_SF analysis_sf; // 同画面参数分析
Base_Function_AD_Check()
{
@ -2169,6 +2324,9 @@ struct Base_Function_AD_Check
analysis_num.Init();
analysis_dis.Init();
analysis_s.Init();
analysis_iou.Init();
analysis_bind.Init();
analysis_sf.Init();
}
void copy(Base_Function_AD_Check tem)
@ -2180,16 +2338,22 @@ 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);
this->analysis_bind.copy(tem.analysis_bind);
this->analysis_sf.copy(tem.analysis_sf);
}
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 %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(),
analysis_bind.GetInfo("analysis_bind").c_str(),
analysis_sf.GetInfo("analysis_sf").c_str());
}
std::string GetInfo(std::string str)
{
@ -2202,7 +2366,114 @@ 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");
str123 += analysis_bind.GetInfo("analysis_time");
str123 += analysis_sf.GetInfo("analysis_sf");
return str123;
}
};
// 盲失检测功能
struct Base_Function_MS_Check
{
bool bOpen; // 是否开启
float MS_IOU; // IOU 阈值(同位置判断)
float MS_Expand; // 扩张值(像素)
std::vector<std::string> ms_main_channel; // 主通道绑定
std::vector<std::string> ms_filter_channels; // 过滤通道绑定
Base_Function_MS_Check()
{
Init();
}
void Init()
{
bOpen = false;
MS_IOU = 0.8f;
MS_Expand = 0.0f;
ms_main_channel.clear();
ms_filter_channels.clear();
}
void copy(Base_Function_MS_Check tem)
{
this->bOpen = tem.bOpen;
this->MS_IOU = tem.MS_IOU;
this->MS_Expand = tem.MS_Expand;
this->ms_main_channel = tem.ms_main_channel;
this->ms_filter_channels = tem.ms_filter_channels;
}
void print(std::string str)
{
printf("%s", GetInfo(str).c_str());
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%s>>bOpen %d MS_IOU %f MS_Expand %f ", str.c_str(), bOpen, MS_IOU, MS_Expand);
std::string str123 = buffer;
for (int i = 0; i < ms_main_channel.size(); i++)
{
str123 += str + ":ms_main_channel " + ms_main_channel.at(i) + ";";
}
for (int i = 0; i < ms_filter_channels.size(); i++)
{
str123 += str + ":ms_filter_channels " + ms_filter_channels.at(i) + ";";
}
str123 += "\n";
return str123;
}
};
// Cell黑白点检测功能
struct Base_Function_CellBW_Check
{
bool bOpen; // 是否开启
float CellBW_IOU; // IOU 阈值(同位置判断)
float CellBW_Expand; // 扩张值(像素)
std::vector<std::string> cellbw_main_channel; // 主通道绑定
std::vector<std::string> cellbw_filter_channels; // 过滤通道绑定
Base_Function_CellBW_Check()
{
Init();
}
void Init()
{
bOpen = false;
CellBW_IOU = 0.8f;
CellBW_Expand = 0.0f;
cellbw_main_channel.clear();
cellbw_filter_channels.clear();
}
void copy(Base_Function_CellBW_Check tem)
{
this->bOpen = tem.bOpen;
this->CellBW_IOU = tem.CellBW_IOU;
this->CellBW_Expand = tem.CellBW_Expand;
this->cellbw_main_channel = tem.cellbw_main_channel;
this->cellbw_filter_channels = tem.cellbw_filter_channels;
}
void print(std::string str)
{
printf("%s", GetInfo(str).c_str());
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%s>>bOpen %d CellBW_IOU %f CellBW_Expand %f ", str.c_str(), bOpen, CellBW_IOU, CellBW_Expand);
std::string str123 = buffer;
for (int i = 0; i < cellbw_main_channel.size(); i++)
{
str123 += str + ":cellbw_main_channel " + cellbw_main_channel.at(i) + ";";
}
for (int i = 0; i < cellbw_filter_channels.size(); i++)
{
str123 += str + ":cellbw_filter_channels " + cellbw_filter_channels.at(i) + ";";
}
str123 += "\n";
return str123;
}
};
@ -2216,6 +2487,9 @@ struct BaseCheckFunction
Base_Function_BigNG bigNG;
Base_Function_EdgeChannel edgeChannel;
Base_Function_AD_Check ad_check;
Base_Function_MS_Check ms_check;
Base_Function_CellBW_Check cellbw_check;
bool bDrawAndSave; // 是否存图绘制(绘制并保存结果图,运行时替代 SAVE_RESIMG 宏)
BaseCheckFunction()
{
Init();
@ -2228,6 +2502,9 @@ struct BaseCheckFunction
bigNG.Init();
edgeChannel.Init();
ad_check.Init();
ms_check.Init();
cellbw_check.Init();
bDrawAndSave = false;
}
void copy(BaseCheckFunction tem)
{
@ -2237,6 +2514,9 @@ struct BaseCheckFunction
this->bigNG.copy(tem.bigNG);
this->edgeChannel.copy(tem.edgeChannel);
this->ad_check.copy(tem.ad_check);
this->ms_check.copy(tem.ms_check);
this->cellbw_check.copy(tem.cellbw_check);
this->bDrawAndSave = tem.bDrawAndSave;
}
void print(std::string str)
{
@ -2247,6 +2527,9 @@ struct BaseCheckFunction
bigNG.print("bigNG");
edgeChannel.print("edgeChannel");
ad_check.print("ad_check");
ms_check.print("ms_check");
cellbw_check.print("cellbw_check");
printf("bDrawAndSave %d\n", bDrawAndSave);
}
std::string GetInfo(std::string str)
{
@ -2257,6 +2540,8 @@ struct BaseCheckFunction
str123 += bigNG.GetInfo("bigNG");
str123 += edgeChannel.GetInfo("edgeChannel");
str123 += ad_check.GetInfo("ad_check");
str123 += ms_check.GetInfo("ms_check");
str123 += cellbw_check.GetInfo("cellbw_check");
// str123 += "\n";
return str123;
}

@ -64,6 +64,8 @@ public:
int GetConfig(ALLChannelCheckFunction &config);
int GetFunction(Json::Value value, CheckFunction &function);
static bool s_bDetTag; // 标签检测模式标志,由命令行 -tag 参数控制
private:
ALLChannelCheckFunction _config;
};

@ -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,8 +163,13 @@ int ConfigManager::UpdateConfig()
printf("Error >>>> camear Name is empty \n");
continue;
}
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();

@ -1,5 +1,8 @@
#include "JsonConfig.h"
// 静态成员定义:标签检测模式标志
bool ChannelFuntonConfigJson::s_bDetTag = false;
Json::Value CommonParamJson::toJsonValue()
{
return Json::Value();
@ -32,7 +35,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))
{
@ -45,10 +48,11 @@ void CommonParamToCheckConfigJson::toObjectFromValue(Json::Value root)
if (value.isObject())
{
_config.baseConfig.strConfigVersion = value["version"].asString();
if ( _config.baseConfig.strConfigVersion == "")
if (_config.baseConfig.strConfigVersion == "")
{
_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();
@ -530,6 +534,10 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
{
function.f_YXDet.strModle = value_f["form"]["YX_Det"]["AI_Model"].asString();
}
if (value_f["form"]["YX_Det"]["minArea"])
{
function.f_YXDet.minArea = value_f["form"]["YX_Det"]["minArea"].asInt();
}
// function.f_UseUpQX.print("UpQX");
}
@ -559,6 +567,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
{
@ -953,83 +982,6 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
}
// getchar();
}
// 暗点检测
if ("AD_Check" == strCode)
{
auto value_f = value[i];
// std::cout << value_f << std::endl;
function.f_AD_Check.bOpen = value_f["isOpen"].asBool();
if (function.f_AD_Check.bOpen)
{
if (value_f["form"]["AD_S_Standard"]["AD_3S_Area"])
{
function.f_AD_Check.S_standard_3s.area = value_f["form"]["AD_S_Standard"]["AD_3S_Area"].asFloat();
}
if (value_f["form"]["AD_S_Standard"]["AD_3S_Len"])
{
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();
}
if (value_f["form"]["AD_S_Standard"]["AD_2S_Len"])
{
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();
}
if (value_f["form"]["AD_S_Standard"]["AD_1S_Len"])
{
function.f_AD_Check.S_standard_1s.len = value_f["form"]["AD_S_Standard"]["AD_1S_Len"].asFloat();
}
if (value_f["form"]["AD_Check_Num"]["Open"])
{
function.f_AD_Check.analysis_num.bOpen = value_f["form"]["AD_Check_Num"]["Open"].asBool();
}
if (value_f["form"]["AD_Check_Num"]["Num"])
{
function.f_AD_Check.analysis_num.numT = value_f["form"]["AD_Check_Num"]["Num"].asInt();
}
if (value_f["form"]["AD_Check_Dis"]["Open"])
{
function.f_AD_Check.analysis_dis.bOpen = value_f["form"]["AD_Check_Dis"]["Open"].asBool();
}
if (value_f["form"]["AD_Check_Dis"]["Dis"])
{
function.f_AD_Check.analysis_dis.disT = value_f["form"]["AD_Check_Dis"]["Dis"].asFloat();
}
if (value_f["form"]["AD_Check_S"]["Open"])
{
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();
}
if (value_f["form"]["AD_Check_S"]["NG_3s"])
{
function.f_AD_Check.analysis_s.NG_3s = value_f["form"]["AD_Check_S"]["NG_3s"].asBool();
}
if (value_f["form"]["AD_Check_S"]["Num"])
{
function.f_AD_Check.analysis_s.Check_s_Num = value_f["form"]["AD_Check_S"]["Num"].asInt();
}
}
else
{
function.f_AD_Check.Init();
}
// getchar();
}
// 异物检测
if ("POL_Cam" == strCode)
{
@ -1083,6 +1035,50 @@ int ChannelFuntonConfigJson::GetFunction(Json::Value value, CheckFunction &funct
// function.f_markDet.print("UpQX");
// getchar();
}
// 标签配置参数
if ("Det_BQ" == strCode)
{
auto value_f = value[i];
// std::cout << value_f << std::endl;
// getchar();
function.f_Det_BQ.bBQ_AI_Det = value_f["isOpen"].asBool();
{
if (value_f["form"]["BQ_Param"]["BQ_expand"])
{
function.f_Det_BQ.BQ_expand = value_f["form"]["BQ_Param"]["BQ_expand"].asInt();
}
if (value_f["form"]["BQ_Param"]["bsaveAll"])
{
function.f_Det_BQ.bsaveAll = value_f["form"]["BQ_Param"]["bsaveAll"].asBool();
}
}
// 当 -tag 参数启用时,强制开启标签 AI 检测和保存所有中间结果
if (s_bDetTag)
{
function.f_Det_BQ.bBQ_AI_Det = true;
function.f_Det_BQ.bsaveAll = true;
}
}
if ("Det_RLine" == strCode)
{
auto value_f = value[i];
// std::cout << value_f << std::endl;
function.f_RLineDet.bOpen = value_f["isOpen"].asBool();
if (function.f_RLineDet.bOpen)
{
if (value_f["form"]["Config_Model_512"]["bOpen"])
{
function.f_RLineDet.bOpen_512 = value_f["form"]["Config_Model_512"]["bOpen"].asBool();
}
}
else
{
function.f_RLineDet.Init();
}
// function.f_RLineDet.print("");
// getchar();
}
}
}
return 0;
@ -1375,6 +1371,7 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
_config.ad_check.bOpen = value_f["isOpen"].asBool();
if (_config.ad_check.bOpen)
{
// S标准
if (value_f["form"]["AD_S_Standard"]["AD_3S_Area"])
{
_config.ad_check.S_standard_3s.area = value_f["form"]["AD_S_Standard"]["AD_3S_Area"].asFloat();
@ -1402,6 +1399,7 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
_config.ad_check.S_standard_1s.len = value_f["form"]["AD_S_Standard"]["AD_1S_Len"].asFloat();
}
// 数量分析
if (value_f["form"]["AD_Check_Num"]["Open"])
{
_config.ad_check.analysis_num.bOpen = value_f["form"]["AD_Check_Num"]["Open"].asBool();
@ -1411,6 +1409,7 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
_config.ad_check.analysis_num.numT = value_f["form"]["AD_Check_Num"]["Num"].asInt();
}
// S数量分析
if (value_f["form"]["AD_Check_Dis"]["Open"])
{
_config.ad_check.analysis_dis.bOpen = value_f["form"]["AD_Check_Dis"]["Open"].asBool();
@ -1420,6 +1419,7 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
_config.ad_check.analysis_dis.disT = value_f["form"]["AD_Check_Dis"]["Dis"].asFloat();
}
// 距离分析
if (value_f["form"]["AD_Check_S"]["Open"])
{
_config.ad_check.analysis_s.bOpen = value_f["form"]["AD_Check_S"]["Open"].asBool();
@ -1437,12 +1437,196 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
{
_config.ad_check.analysis_s.Check_s_Num = value_f["form"]["AD_Check_S"]["Num"].asInt();
}
// IOU分析
if (value_f["form"]["AD_Check_IOU"]["AD_IOU"])
{
_config.ad_check.analysis_iou.AD_IOU = value_f["form"]["AD_Check_IOU"]["AD_IOU"].asFloat();
}
if (value_f["form"]["AD_Check_IOU"]["AD_Expand"])
{
_config.ad_check.analysis_iou.AD_Expand = value_f["form"]["AD_Check_IOU"]["AD_Expand"].asFloat();
}
// 通道绑定
if (value_f["form"]["Bind_Channel"]["bUse_bind"])
{
_config.ad_check.analysis_bind.bUse = value_f["form"]["Bind_Channel"]["bUse_bind"].asBool();
}
auto value_qx = value_f["form"]["Bind_Channel"]["2s_main_channel"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ad_check.analysis_bind.main_channel_2s.push_back(channel);
}
}
value_qx = value_f["form"]["Bind_Channel"]["2s_second_channel"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ad_check.analysis_bind.second_channel_2s.push_back(channel);
}
}
value_qx = value_f["form"]["Bind_Channel"]["3s_main_channel"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ad_check.analysis_bind.main_channel_3s.push_back(channel);
}
}
value_qx = value_f["form"]["Bind_Channel"]["3s_second_channel"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ad_check.analysis_bind.second_channel_3s.push_back(channel);
}
}
value_qx = value_f["form"]["Bind_Channel"]["correlated_channels"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ad_check.analysis_bind.correlated_channels.push_back(channel);
}
}
// 关联过滤源缺陷类型(如 ["Dirty_L0","Dirty_L1","Dirty_L2","AD"])
value_qx = value_f["form"]["Bind_Channel"]["correlated_types"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string qx_type = value_qx[idx].asString();
_config.ad_check.analysis_bind.correlated_types.push_back(qx_type);
}
}
// 同画面分析
value_qx = value_f["form"]["Single_Channel"]["s_channel"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ad_check.analysis_sf.s_sf.push_back(channel);
}
}
value_qx = value_f["form"]["Single_Channel"]["region1_channel"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ad_check.analysis_sf.region_sf.push_back(channel);
}
}
}
else
{
_config.ad_check.Init();
}
}
// 是否存图绘制开关:控制结果图绘制与保存(运行时替代 SAVE_RESIMG 宏)
if ("bDrawAndSave" == strCode)
{
_config.bDrawAndSave = value["isOpen"].asBool();
}
// 盲失分析(code绑定的qx_Other)
if ("MS_Analysis" == strCode)
{
auto value_f = value;
// std::cout << value_f << std::endl;
_config.ms_check.bOpen = value_f["isOpen"].asBool();
if (_config.ms_check.bOpen)
{
// IOU分析
if (value_f["form"]["MS_Check_Param"]["ms_iou"])
{
_config.ms_check.MS_IOU = value_f["form"]["MS_Check_Param"]["ms_iou"].asFloat();
}
if (value_f["form"]["MS_Check_Param"]["ms_expand"])
{
_config.ms_check.MS_Expand = value_f["form"]["MS_Check_Param"]["ms_expand"].asFloat();
}
// 通道绑定
auto value_qx = value_f["form"]["MS_Check_Param"]["ms_main_channels"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ms_check.ms_main_channel.push_back(channel);
}
}
value_qx = value_f["form"]["MS_Check_Param"]["ms_filter_channels"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.ms_check.ms_filter_channels.push_back(channel);
}
}
}
else
{
_config.ms_check.Init();
}
}
// Cell黑白点分析(code绑定的qx_Other)
if ("Cellbw_Analysis" == strCode)
{
auto value_f = value;
// std::cout << value_f << std::endl;
_config.cellbw_check.bOpen = value_f["isOpen"].asBool();
if (_config.cellbw_check.bOpen)
{
// IOU分析
if (value_f["form"]["CellBW_Check_Param"]["cellbw_iou"])
{
_config.cellbw_check.CellBW_IOU = value_f["form"]["CellBW_Check_Param"]["cellbw_iou"].asFloat();
}
if (value_f["form"]["CellBW_Check_Param"]["cellbw_expand"])
{
_config.cellbw_check.CellBW_Expand = value_f["form"]["CellBW_Check_Param"]["cellbw_expand"].asFloat();
}
// 通道绑定
auto value_qx = value_f["form"]["CellBW_Check_Param"]["cellbw_main_channels"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.cellbw_check.cellbw_main_channel.push_back(channel);
}
}
value_qx = value_f["form"]["CellBW_Check_Param"]["cellbw_filter_channels"];
if (value_qx.isArray())
{
for (int idx = 0; idx < value_qx.size(); idx++)
{
std::string channel = value_qx[idx].asString();
_config.cellbw_check.cellbw_filter_channels.push_back(channel);
}
}
}
else
{
_config.cellbw_check.Init();
}
}
return 0;
}

@ -14,6 +14,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/AlgorithmModule/include
${PROJECT_SOURCE_DIR}/ConfigModule/include
${PROJECT_SOURCE_DIR}/AIEngineModule/include_base
)
link_directories(

@ -0,0 +1,177 @@
#ifndef _MEM_MONITOR_H_
#define _MEM_MONITOR_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.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)
// 获取 GPU 显存使用情况(通过 nvidia-smi),返回所有 GPU 累加的已用/总量,单位 MB
// 成功返回 0,失败返回 -1(此时 usedMB/totalMB 置为 -1)
static inline int getGPUMemInfo(long &usedMB, long &totalMB)
{
usedMB = -1;
totalMB = -1;
FILE *fp = popen("nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader,nounits 2>/dev/null", "r");
if (!fp)
{
return -1;
}
char line[256];
long used = 0;
long total = 0;
int n = 0;
while (fgets(line, sizeof(line), fp))
{
long u = 0;
long t = 0;
if (sscanf(line, "%ld, %ld", &u, &t) == 2)
{
used += u;
total += t;
n++;
}
}
pclose(fp);
if (n == 0)
{
return -1;
}
usedMB = used;
totalMB = total;
return 0;
}
// 获取系统内存信息(/proc/meminfo),单位 KB
// 成功返回 0,失败返回 -1(此时各值置为 -1)
static inline int getSystemMemInfo(long &totalKB, long &freeKB, long &availableKB)
{
totalKB = -1;
freeKB = -1;
availableKB = -1;
FILE *fp = fopen("/proc/meminfo", "r");
if (!fp)
{
return -1;
}
char line[256];
while (fgets(line, sizeof(line), fp))
{
long val = -1;
if (sscanf(line, "MemTotal: %ld kB", &val) == 1)
{
totalKB = val;
}
else if (sscanf(line, "MemFree: %ld kB", &val) == 1)
{
freeKB = val;
}
else if (sscanf(line, "MemAvailable: %ld kB", &val) == 1)
{
availableKB = val;
}
}
fclose(fp);
return 0;
}
// 系统 CPU 时间快照(/proc/stat 第一行 cpu 累计值),用于两次采样计算 CPU 使用率
struct CPUStat
{
unsigned long long user;
unsigned long long nice;
unsigned long long system;
unsigned long long idle;
unsigned long long iowait;
unsigned long long irq;
unsigned long long softirq;
unsigned long long steal;
CPUStat()
{
user = nice = system = idle = iowait = irq = softirq = steal = 0;
}
unsigned long long total() const
{
return user + nice + system + idle + iowait + irq + softirq + steal;
}
unsigned long long idleTotal() const
{
return idle + iowait;
}
};
// 读取系统 CPU 时间快照,成功返回 0,失败返回 -1
static inline int getSystemCPUStat(CPUStat &stat)
{
FILE *fp = fopen("/proc/stat", "r");
if (!fp)
{
return -1;
}
char line[256];
int ret = -1;
if (fgets(line, sizeof(line), fp))
{
// 格式: cpu user nice system idle iowait irq softirq steal guest guest_nice
if (sscanf(line, "cpu %llu %llu %llu %llu %llu %llu %llu %llu",
&stat.user, &stat.nice, &stat.system, &stat.idle,
&stat.iowait, &stat.irq, &stat.softirq, &stat.steal) >= 4)
{
ret = 0;
}
}
fclose(fp);
return ret;
}
// 进程累计消耗的 CPU 时间(用户态 + 内核态),单位秒
static inline double cpuTimeSec(const struct rusage &usage)
{
return (double)usage.ru_utime.tv_sec + (double)usage.ru_utime.tv_usec / 1e6 +
(double)usage.ru_stime.tv_sec + (double)usage.ru_stime.tv_usec / 1e6;
}
#endif // _MEM_MONITOR_H_

@ -46,6 +46,7 @@ struct SystemConfigParam
std::string config_Root_Path;
std::string save_Img_Root_Path; // 检测结果图保存根目录
int Use_CPU_StartIdx; // 使用 CPU 的开始核
int nWorkIdx; // 工位号
@ -54,6 +55,7 @@ struct SystemConfigParam
{
Use_CPU_StartIdx = 0;
config_Root_Path = "";
save_Img_Root_Path = "/home/aidlux/BOE/FOG/Detsave/";
nWorkIdx = 0;
for(int i=0; i<4; i++) GPU_Device_ID_List[i] = -1;
}

@ -3,10 +3,15 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctime>
#include "MemMonitor.h"
#include <unistd.h>
#include <string>
#include <algorithm>
#include "CheckUtil.hpp"
#include "LoadImage.hpp" // #include "LoadImage.h"
#include "JsonConfig.h"
#include "AI_Factory.h"
bool check_strChannel(string strChannel){
if(strChannel == "XD" || strChannel == "XON") return false;
@ -260,6 +265,9 @@ int deal::Init()
// 初始化参数模块
InitConfig();
// 设置标签检测模式标志,供 ChannelFuntonConfigJson 在解析 JSON 时使用
ChannelFuntonConfigJson::s_bDetTag = runConfig.det_Tag;
printf("\n\n\n\n\n\n\n\n\n\n\n**********************************************************LoadCheckImgConfig\n");
// 载入参数
re = LoadCheckImgConfig();
@ -833,7 +841,7 @@ void deal::ResultThread(int id)
if (true)
{
// 存图
std::string savepath = "/home/aidlux/BOE/FOG/Detsave/";
std::string savepath = m_system_param.save_Img_Root_Path;
saveImg(savepath, dealResult);
// saveImg("/home/aidlux/BOE/ResultImg/", dealResult);
}
@ -842,7 +850,7 @@ void deal::ResultThread(int id)
// 存 检测统计 日志
mutex_DetResult_.lock();
m_DetResult.print(false);
std::string strlog = "/home/aidlux/BOE/FOG/Detsave/";
std::string strlog = m_system_param.save_Img_Root_Path;
if (m_strCurDate != "")
{
strlog += m_strCurDate;
@ -856,6 +864,10 @@ void deal::ResultThread(int id)
writeLog(strlog, m_DetResult.strlist);
mutex_DetResult_.unlock();
}
// ✅ 递增已保存结果计数,并通知等待线程,立刻唤醒检查
m_nProductSavedResultCount.fetch_add(1);
m_productResultCV.notify_all();
}
usleep(5 * 1000);
@ -930,6 +942,10 @@ bool deal::ReadSystemConfig(const std::string &strPath)
m_system_param.Use_CPU_StartIdx = root["Use_CPU_StartIdx"].asInt();
m_system_param.nWorkIdx = root["Detect_Work_Idx"].asInt();
m_system_param.config_Root_Path = root["Config_Root_Path"].asString();
if (root.isMember("Save_Img_Root_Path"))
{
m_system_param.save_Img_Root_Path = root["Save_Img_Root_Path"].asString();
}
for (int i = 0; i < 4; i++)
m_system_param.GPU_Device_ID_List[i] = -1;
@ -1289,6 +1305,15 @@ int deal::DetImg()
return 0;
}
// AI 检测独立测试:读取 in 图 -> 模型推理 -> 保存 out 图(不做其他流程)
if (runConfig.det_Type == RUNTYPE_RUN_File_AI_DetTest)
{
m_nReadThread_type = READ_THREAD_TYPE_READIMG;
m_nSystemCheckType = CHECK_TYPE_OFFLING;
TestAIDet();
return 0;
}
m_nReadThread_type = READ_THREAD_TYPE_READIMG;
m_nSystemCheckType = CHECK_TYPE_OFFLING;
@ -1306,6 +1331,657 @@ int deal::DetImg()
return 0;
}
/// @brief 监控模式:4 线程并发循环执行模型推理(压 GPU),并把当前设备资源消耗
/// (CPU 内存 / GPU 显存)写入 monitor.log;正常(out 全黑)不写图,检测到残点
/// (out 图至少一个像素不为全黑)时才保存对应 *_out.png 并退出监控。
/// @return 0 表示成功,非 0 表示初始化失败
int deal::MonitorRun()
{
// 输入目录:优先使用命令行指定路径,否则使用默认路径
std::string strSrcRoot = runConfig.filePath;
if (strSrcRoot.empty())
{
strSrcRoot = "/home/aidlux/BOE/FOG/AI_Detect/";
}
else if (strSrcRoot.back() != '/')
{
strSrcRoot += "/";
}
std::shared_ptr<AIFactory> pAIFactory = AIFactory::GetInstance();
std::shared_ptr<AIModel_Base> pAI_Model = pAIFactory->AI_defect_NF;
if (!pAI_Model || !pAI_Model->m_bInitSuccess)
{
printf("MonitorRun: AI model (AI_defect_NF) not ready!\n");
return 1;
}
const int nModelInCh = pAI_Model->input_0.channel;
const int nModelInW = pAI_Model->input_0.width;
const int nModelInH = pAI_Model->input_0.height;
printf("MonitorRun: model input [c=%d w=%d h=%d] output [c=%d w=%d h=%d]\n",
nModelInCh, nModelInW, nModelInH,
pAI_Model->output_0.channel, pAI_Model->output_0.width, pAI_Model->output_0.height);
// 收集所有 *_in.png 文件
std::vector<std::string> inList;
try
{
if (!fs::exists(strSrcRoot))
{
printf("MonitorRun: src dir not exist: %s\n", strSrcRoot.c_str());
return 1;
}
for (const auto &entry : fs::recursive_directory_iterator(strSrcRoot))
{
if (!entry.is_regular_file())
{
continue;
}
std::string name = entry.path().filename().string();
const std::string suffix = "_in.png";
if (name.size() >= suffix.size() &&
name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0)
{
inList.push_back(entry.path().string());
}
}
}
catch (const std::exception &e)
{
printf("MonitorRun: traverse error %s\n", e.what());
return 1;
}
std::sort(inList.begin(), inList.end());
printf("MonitorRun: total %zu in images\n", inList.size());
if (inList.empty())
{
printf("MonitorRun: no *_in.png found\n");
return 1;
}
// 输出目录:输入目录去掉末尾 '/' 后追加 "_TEST/"(与 TestAIDet 保持一致)
std::string strDstRoot = strSrcRoot;
while (strDstRoot.size() > 1 && strDstRoot.back() == '/')
{
strDstRoot.pop_back();
}
strDstRoot += "_TEST/";
{
std::error_code ec;
fs::create_directories(strDstRoot, ec);
}
// 预处理成模型输入(只读一次,每轮复用),并计算对应输出路径(*_in.png -> *_out.png)
std::vector<std::string> okInList;
std::vector<cv::Mat> inImgs;
std::vector<std::string> outList;
okInList.reserve(inList.size());
inImgs.reserve(inList.size());
outList.reserve(inList.size());
for (size_t i = 0; i < inList.size(); i++)
{
cv::Mat inImg = cv::imread(inList[i], cv::IMREAD_UNCHANGED);
if (inImg.empty())
{
printf("MonitorRun: read fail: %s\n", inList[i].c_str());
continue;
}
if (inImg.channels() != nModelInCh)
{
cv::Mat tmp;
if (inImg.channels() >= 3)
{
cv::cvtColor(inImg, tmp, cv::COLOR_BGR2GRAY);
}
else
{
tmp = inImg.clone();
}
inImg = tmp;
}
if (inImg.type() != CV_8U)
{
inImg.convertTo(inImg, CV_8U);
}
if (inImg.cols != nModelInW || inImg.rows != nModelInH)
{
cv::resize(inImg, inImg, cv::Size(nModelInW, nModelInH));
}
okInList.push_back(inList[i]);
inImgs.push_back(inImg);
std::string rel = inList[i].substr(strSrcRoot.size());
const std::string inSuffix = "_in.png";
const std::string outSuffix = "_out.png";
size_t pos = rel.rfind(inSuffix);
if (pos != std::string::npos)
{
rel.replace(pos, inSuffix.size(), outSuffix);
}
outList.push_back(strDstRoot + rel);
}
if (inImgs.empty())
{
printf("MonitorRun: no valid in image\n");
return 1;
}
// 打开监控日志(追加写)
std::string strLogPath = strDstRoot + "monitor.log";
std::ofstream logFile(strLogPath, std::ios::app);
if (!logFile)
{
printf("MonitorRun: open log fail: %s\n", strLogPath.c_str());
return 1;
}
// 日志互斥锁(worker 线程与主线程都会写日志)
std::mutex logMutex;
auto logLine = [&logFile, &logMutex](const std::string &line)
{
std::lock_guard<std::mutex> lock(logMutex);
printf("%s\n", line.c_str());
fflush(stdout); // 强制刷新控制台,避免 stdout 块缓冲导致显示延迟
logFile << line << std::endl;
};
const int nThreadNum = 4; // 并发线程数(与模型 Stream_num 一致时并行度最高)
long nImageNum = (long)inImgs.size();
// 共享状态
std::atomic<long> nNextIdx{0}; // 原子分配任务索引,轮询所有图
std::atomic<long> nInferCount{0}; // 成功推理次数
std::atomic<long> nFailCount{0}; // 推理失败次数
std::atomic<bool> bHit{false}; // 是否命中非全黑 out
std::atomic<long> nHitIdx{-1}; // 命中的图索引
std::atomic<int> nHitNonZero{0}; // 命中的非零像素数
std::mutex saveMutex; // 保护 out 文件写入
logLine("===== monitor start (4 threads loop, exit on first non-black out) =====");
// 启动 worker 线程:循环推理压 GPU
std::vector<std::thread> workers;
workers.reserve(nThreadNum);
for (int t = 0; t < nThreadNum; t++)
{
workers.emplace_back([&, t]()
{
while (!bHit.load(std::memory_order_relaxed))
{
long idx = nNextIdx.fetch_add(1, std::memory_order_relaxed) % nImageNum;
cv::Mat outImg;
int re = pAI_Model->AIDet(inImgs[(size_t)idx], outImg);
if (re != 0 || outImg.empty())
{
nFailCount.fetch_add(1, std::memory_order_relaxed);
continue;
}
nInferCount.fetch_add(1, std::memory_order_relaxed);
int nNonZero = cv::countNonZero(outImg);
if (nNonZero > 0)
{
// 有残点才写图,然后退出
bool expected = false;
if (bHit.compare_exchange_strong(expected, true))
{
{
std::lock_guard<std::mutex> lock(saveMutex);
fs::path outParent = fs::path(outList[(size_t)idx]).parent_path();
if (!outParent.empty())
{
std::error_code ec;
fs::create_directories(outParent, ec);
}
cv::imwrite(outList[(size_t)idx], outImg);
}
nHitIdx.store(idx, std::memory_order_relaxed);
nHitNonZero.store(nNonZero, std::memory_order_relaxed);
logLine("[hit] thread " + std::to_string(t) + " out image " +
okInList[(size_t)idx] + " non-zero pixels: " +
std::to_string(nNonZero) + " -> " + outList[(size_t)idx]);
}
break;
}
}
});
}
// 主线程:资源监控(持续循环采样),直到命中退出
struct rusage usagePrev;
getrusage(RUSAGE_SELF, &usagePrev);
CPUStat cpuStatPrev;
getSystemCPUStat(cpuStatPrev);
auto wallPrev = std::chrono::steady_clock::now();
long nRound = 0;
// GPU 显存采样很慢(内部执行 nvidia-smi),降低采样频率避免阻塞主循环
long gpuUsedMB = -1;
long gpuTotalMB = -1;
const int nGPUSampleInterval = 10; // 每 10 轮(约 10 秒)才调用一次 nvidia-smi
while (!bHit.load(std::memory_order_relaxed))
{
nRound++;
// 1. 采集设备资源消耗
long rssKB = getRSS_KB();
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
long memTotalKB = -1;
long memFreeKB = -1;
long memAvailKB = -1;
getSystemMemInfo(memTotalKB, memFreeKB, memAvailKB);
CPUStat cpuStat;
getSystemCPUStat(cpuStat);
if ((nRound % nGPUSampleInterval) == 1)
{
getGPUMemInfo(gpuUsedMB, gpuTotalMB);
}
auto wallNow = std::chrono::steady_clock::now();
// 系统 CPU 使用率(两次采样之间)
double sysCpuUsage = -1.0;
unsigned long long totalDelta = cpuStat.total() - cpuStatPrev.total();
unsigned long long idleDelta = cpuStat.idleTotal() - cpuStatPrev.idleTotal();
if (totalDelta > 0)
{
sysCpuUsage = 100.0 * (1.0 - (double)idleDelta / (double)totalDelta);
}
// 进程 CPU 使用率(getrusage 的 CPU 时间 / 墙钟时间,多核下可能超过 100%)
double procCpuUsage = -1.0;
double wallSec = std::chrono::duration<double>(wallNow - wallPrev).count();
if (wallSec > 0)
{
procCpuUsage = 100.0 * (cpuTimeSec(usage) - cpuTimeSec(usagePrev)) / wallSec;
}
// 更新采样基准
usagePrev = usage;
cpuStatPrev = cpuStat;
wallPrev = wallNow;
time_t now = time(NULL);
struct tm tmv;
localtime_r(&now, &tmv);
char ts[64] = {0};
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", &tmv);
char buf[512];
snprintf(buf, sizeof(buf),
"[round %ld] %s | infer=%ld fail=%ld | VmRSS: %ld MB | maxRSS: %ld MB | ProcCPU: %.1f%% | SysCPU: %.1f%% | Mem total %ld / free %ld / avail %ld MB | GPU: %ld/%ld MB",
nRound, ts, nInferCount.load(), nFailCount.load(),
rssKB / 1024, usage.ru_maxrss / 1024,
procCpuUsage, sysCpuUsage,
memTotalKB / 1024, memFreeKB / 1024, memAvailKB / 1024,
gpuUsedMB, gpuTotalMB);
// 打印降频:每 10 轮打印一次,减少日志 IO/系统调用对 CPU 的争抢
if ((nRound % 10) == 1)
{
logLine(buf);
}
}
// 等待所有 worker 线程退出
for (auto &th : workers)
{
if (th.joinable())
{
th.join();
}
}
long hitIdx = nHitIdx.load();
logLine("===== monitor exit (hit image " +
(hitIdx >= 0 ? okInList[(size_t)hitIdx] : std::string("?")) +
", non-zero pixels " + std::to_string(nHitNonZero.load()) + ") =====");
logFile.close();
return 0;
}
/// @brief AI 检测独立测试:遍历 /home/aidlux/BOE/FOG/AI_Detect/ 下所有 *_in.png,
/// 送入 AI_defect_NF 模型推理,将输出 *_out.png 保存到 /home/aidlux/BOE/FOG/AI_Detect_TEST/
/// @return 0 表示成功
/*int deal::TestAIDet()
{
// 输入目录:优先使用命令行指定路径,否则使用默认路径
std::string strSrcRoot = runConfig.filePath;
if (strSrcRoot.empty())
{
strSrcRoot = "/home/aidlux/BOE/FOG/AI_Detect/";
}
else if (strSrcRoot.back() != '/')
{
strSrcRoot += "/";
}
// 输出目录:输入目录去掉末尾 '/' 后追加 "_TEST/"
// 默认输入 /home/aidlux/BOE/FOG/AI_Detect/ -> 输出 /home/aidlux/BOE/FOG/AI_Detect_TEST/
std::string strDstRoot = strSrcRoot;
while (strDstRoot.size() > 1 && strDstRoot.back() == '/')
{
strDstRoot.pop_back();
}
strDstRoot += "_TEST/";
if(runConfig.bmonitor)
{
// 监控模式:每 5 分钟执行一次模型推理,记录设备资源消耗到 log;
// 当某次推理输出 out 图至少一个像素不为全黑时退出监控。
return MonitorRun();
}
std::shared_ptr<AIFactory> pAIFactory = AIFactory::GetInstance();
std::shared_ptr<AIModel_Base> pAI_Model = pAIFactory->AI_defect_NF;
if (!pAI_Model || !pAI_Model->m_bInitSuccess)
{
printf("TestAIDet: AI model (AI_defect_NF) not ready!\n");
return 1;
}
printf("TestAIDet: model input [c=%d w=%d h=%d] output [c=%d w=%d h=%d]\n",
pAI_Model->input_0.channel, pAI_Model->input_0.width, pAI_Model->input_0.height,
pAI_Model->output_0.channel, pAI_Model->output_0.width, pAI_Model->output_0.height);
// 收集所有 *_in.png 文件
std::vector<std::string> inList;
try
{
if (!fs::exists(strSrcRoot))
{
printf("TestAIDet: src dir not exist: %s\n", strSrcRoot.c_str());
return 1;
}
for (const auto &entry : fs::recursive_directory_iterator(strSrcRoot))
{
if (!entry.is_regular_file())
{
continue;
}
std::string path = entry.path().string();
std::string name = entry.path().filename().string();
const std::string suffix = "_in.png";
if (name.size() >= suffix.size() &&
name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0)
{
inList.push_back(path);
}
}
}
catch (const std::exception &e)
{
printf("TestAIDet: traverse error %s\n", e.what());
return 1;
}
std::sort(inList.begin(), inList.end());
printf("TestAIDet: total %zu in images\n", inList.size());
const int nModelInCh = pAI_Model->input_0.channel;
const int nModelInW = pAI_Model->input_0.width;
const int nModelInH = pAI_Model->input_0.height;
int nOK = 0;
for (size_t i = 0; i < inList.size(); i++)
{
const std::string &inPath = inList[i];
cv::Mat inImg = cv::imread(inPath, cv::IMREAD_UNCHANGED);
if (inImg.empty())
{
printf("[%zu/%zu] read fail: %s\n", i + 1, inList.size(), inPath.c_str());
continue;
}
// 统一成模型要求的输入:单通道 8U、模型输入尺寸
if (inImg.channels() != nModelInCh)
{
cv::Mat tmp;
if (inImg.channels() >= 3)
{
cv::cvtColor(inImg, tmp, cv::COLOR_BGR2GRAY);
}
else
{
tmp = inImg.clone();
}
inImg = tmp;
}
if (inImg.type() != CV_8U)
{
inImg.convertTo(inImg, CV_8U);
}
if (inImg.cols != nModelInW || inImg.rows != nModelInH)
{
cv::resize(inImg, inImg, cv::Size(nModelInW, nModelInH));
}
cv::Mat outImg;
int re = pAI_Model->AIDet(inImg, outImg);
if (re != 0 || outImg.empty())
{
printf("[%zu/%zu] infer fail(%d): %s\n", i + 1, inList.size(), re, inPath.c_str());
continue;
}
// 输出 resize 回模型输入尺寸(与 AI_Detect_QX 保存的 out 图尺寸保持一致)
if (outImg.cols != nModelInW || outImg.rows != nModelInH)
{
cv::resize(outImg, outImg, cv::Size(nModelInW, nModelInH), 0, 0, 0);
}
// 相对路径,并保持子目录结构
std::string rel = inPath.substr(strSrcRoot.size());
std::string outRel = rel;
const std::string inSuffix = "_in.png";
const std::string outSuffix = "_out.png";
size_t pos = outRel.rfind(inSuffix);
if (pos != std::string::npos)
{
outRel.replace(pos, inSuffix.size(), outSuffix);
}
std::string outPath = strDstRoot + outRel;
// 创建输出父目录
fs::path outParent = fs::path(outPath).parent_path();
if (!outParent.empty())
{
std::error_code ec;
fs::create_directories(outParent, ec);
}
if (cv::imwrite(outPath, outImg))
{
nOK++;
printf("[%zu/%zu] ok: %s -> %s\n", i + 1, inList.size(), rel.c_str(), outRel.c_str());
}
else
{
printf("[%zu/%zu] save fail: %s\n", i + 1, inList.size(), outPath.c_str());
}
}
printf("TestAIDet: complete, success %d / %zu\n", nOK, inList.size());
return 0;
}*/
/// @brief AI 检测独立测试:遍历 /home/aidlux/BOE/FOG/AI_Detect/ 下所有 *_in.png,
/// 送入 AI_defect_NF 模型推理,将输出 *_out.png 保存到 /home/aidlux/BOE/FOG/AI_Detect_TEST/
/// @return 0 表示成功
int deal::TestAIDet()
{
// 输入目录:优先使用命令行指定路径,否则使用默认路径
std::string strSrcRoot = runConfig.filePath;
if (strSrcRoot.empty())
{
strSrcRoot = "/home/aidlux/BOE/FOG/AI_Detect/";
}
else if (strSrcRoot.back() != '/')
{
strSrcRoot += "/";
}
// 输出目录:输入目录去掉末尾 '/' 后追加 "_TEST/"
// 默认输入 /home/aidlux/BOE/FOG/AI_Detect/ -> 输出 /home/aidlux/BOE/FOG/AI_Detect_TEST/
std::string strDstRoot = strSrcRoot;
while (strDstRoot.size() > 1 && strDstRoot.back() == '/')
{
strDstRoot.pop_back();
}
strDstRoot += "_TEST/";
if(runConfig.bmonitor)
{
// 监控模式:每 5 分钟执行一次模型推理,记录设备资源消耗到 log;
// 当某次推理输出 out 图至少一个像素不为全黑时退出监控。
return MonitorRun();
}
std::shared_ptr<AIFactory> pAIFactory = AIFactory::GetInstance();
std::shared_ptr<AIModel_Base> pAI_Model = pAIFactory->AI_defect_NF;
if (!pAI_Model || !pAI_Model->m_bInitSuccess)
{
printf("TestAIDet: AI model (AI_defect_NF) not ready!\n");
return 1;
}
printf("TestAIDet: model input [c=%d w=%d h=%d] output [c=%d w=%d h=%d]\n",
pAI_Model->input_0.channel, pAI_Model->input_0.width, pAI_Model->input_0.height,
pAI_Model->output_0.channel, pAI_Model->output_0.width, pAI_Model->output_0.height);
// 收集所有 *_in.png 文件
std::vector<std::string> inList;
try
{
if (!fs::exists(strSrcRoot))
{
printf("TestAIDet: src dir not exist: %s\n", strSrcRoot.c_str());
return 1;
}
for (const auto &entry : fs::recursive_directory_iterator(strSrcRoot))
{
if (!entry.is_regular_file())
{
continue;
}
std::string path = entry.path().string();
std::string name = entry.path().filename().string();
const std::string suffix = "_in.png";
if (name.size() >= suffix.size() &&
name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0)
{
inList.push_back(path);
}
}
}
catch (const std::exception &e)
{
printf("TestAIDet: traverse error %s\n", e.what());
return 1;
}
std::sort(inList.begin(), inList.end());
printf("TestAIDet: total %zu in images\n", inList.size());
const int nModelInCh = pAI_Model->input_0.channel;
const int nModelInW = pAI_Model->input_0.width;
const int nModelInH = pAI_Model->input_0.height;
int nOK = 0;
for (size_t i = 0; i < inList.size(); i++)
{
const std::string &inPath = inList[i];
cv::Mat inImg = cv::imread(inPath, cv::IMREAD_UNCHANGED);
if (inImg.empty())
{
printf("[%zu/%zu] read fail: %s\n", i + 1, inList.size(), inPath.c_str());
continue;
}
// 统一成模型要求的输入:单通道 8U、模型输入尺寸
if (inImg.channels() != nModelInCh)
{
cv::Mat tmp;
if (inImg.channels() >= 3)
{
cv::cvtColor(inImg, tmp, cv::COLOR_BGR2GRAY);
}
else
{
tmp = inImg.clone();
}
inImg = tmp;
}
if (inImg.type() != CV_8U)
{
inImg.convertTo(inImg, CV_8U);
}
if (inImg.cols != nModelInW || inImg.rows != nModelInH)
{
cv::resize(inImg, inImg, cv::Size(nModelInW, nModelInH));
}
cv::Mat outImg;
int re = pAI_Model->AIDet(inImg, outImg);
if (re != 0 || outImg.empty())
{
printf("[%zu/%zu] infer fail(%d): %s\n", i + 1, inList.size(), re, inPath.c_str());
continue;
}
// 输出 resize 回模型输入尺寸(与 AI_Detect_QX 保存的 out 图尺寸保持一致)
if (outImg.cols != nModelInW || outImg.rows != nModelInH)
{
cv::resize(outImg, outImg, cv::Size(nModelInW, nModelInH), 0, 0, 0);
}
// 相对路径,并保持子目录结构
std::string rel = inPath.substr(strSrcRoot.size());
std::string outRel = rel;
const std::string inSuffix = "_in.png";
const std::string outSuffix = "_out.png";
size_t pos = outRel.rfind(inSuffix);
if (pos != std::string::npos)
{
outRel.replace(pos, inSuffix.size(), outSuffix);
}
std::string outPath = strDstRoot + outRel;
// 创建输出父目录
fs::path outParent = fs::path(outPath).parent_path();
if (!outParent.empty())
{
std::error_code ec;
fs::create_directories(outParent, ec);
}
if (cv::imwrite(outPath, outImg))
{
nOK++;
printf("[%zu/%zu] ok: %s -> %s\n", i + 1, inList.size(), rel.c_str(), outRel.c_str());
}
else
{
printf("[%zu/%zu] save fail: %s\n", i + 1, inList.size(), outPath.c_str());
}
}
printf("TestAIDet: complete, success %d / %zu\n", nOK, inList.size());
return 0;
}
/// @brief 检查文件图片
/// @detail 后续实现具体功能
/// @return 0 表示成功
@ -1341,11 +2017,105 @@ 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]); // 处理单个产品
// ✅ 等待当前产品的所有检测结果保存完成,再处理下一个产品
// 使用条件变量:ResultThread 每保存完一个结果就 notify_all,立刻唤醒检查
printf("\n[同步] 等待产品 %s 检测结果保存完成...\n", products[i].product_id.c_str());
{
std::unique_lock<std::mutex> lock(mutex_Result_list);
int waitCount = 0;
while (true)
{
bool done = m_productResultCV.wait_for(lock, std::chrono::milliseconds(5000), [this]() {
return m_nProductSavedResultCount.load() >= m_nProductSuccessCount.load()
&& m_Result_list.empty();
});
if (done) break;
waitCount++;
printf("[同步] 等待中... 已保存: %d/%d (成功:%d 失败:%d), 结果队列: %s\n",
m_nProductSavedResultCount.load(),
m_nProductSuccessCount.load() + m_nProductFailCount.load(),
m_nProductSuccessCount.load(), m_nProductFailCount.load(),
m_Result_list.empty() ? "空" : "非空");
}
}
printf("[同步] 产品 %s 所有结果已保存完成 (saved=%d, success=%d)\n",
products[i].product_id.c_str(),
m_nProductSavedResultCount.load(),
m_nProductSuccessCount.load());
// ✅ 释放资源:清理图像数据,释放内存
{
// 清理图像信息列表(已在 CheckProduct 开头清理过,这里做双重保障)
for (auto &info : m_ImageInfoList)
{
if (!info.img.empty())
{
info.img.release();
}
}
m_ImageInfoList.clear();
std::vector<JC_IMAGE_INFO_>().swap(m_ImageInfoList); // 强制释放 vector 内存
}
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: 后续实现具体功能
@ -1383,9 +2153,15 @@ int deal::CheckProduct(const LoadProductImages &product)
m_nProductPendingTasks = 0;
m_nProductSuccessCount = 0;
m_nProductFailCount = 0;
m_nProductSavedResultCount = 0; // 重置已保存结果计数
m_ProductFailedList.clear();
m_nCheckNotifiedCount = 0;
// ✅ 修复内存泄漏:清理上一个产品累积的原始图像数据(cv::Mat)
// 注意:m_DetResult 是统计结果(轻量字符串/数字),不清除,跨产品累积
m_ImageInfoList.clear();
m_productIdList.clear();
// 3. 打印产品信息
printf("\n");
printf("╔══════════════════════════════════════════════════════════\n");

@ -138,6 +138,7 @@ enum RUNTYPE_
RUNTYPE_RUN_File_AI_TEST,
RUNTYPE_RUN_File_Edge_Test_Save_Qx,
RUNTYPE_RUN_File_Edge_Test_Save_ALL,
RUNTYPE_RUN_File_AI_DetTest, // AI 检测独立测试:读 in 图 -> 推理 -> 存 out 图
};
// 程序运行模式
enum Process_Run_Type
@ -545,7 +546,9 @@ struct DealRunConfig
Process_Run_Type run_Type = Process_Run_Detect; // 运行模式
RUNTYPE_ det_Type = RUNTYPE_RUN_Pre; // 处理
std::string filePath = ""; // 处理文件路径 如果为空 处理当前的单张图片
bool det_Tag = false; // 是否检测标签
bool bDebugSaveImg = false; // 是否中间图片图片
bool bmonitor = false; // 是否监控
int m_nTestNum = 999999;
void print(std::string str)
{
@ -632,6 +635,8 @@ private:
int ReJson();
int ReJson_ALL();
int DetImg(); // 套图复测
int TestAIDet(); // AI 检测独立测试:读取 in 图送入模型,保存 out 图
int MonitorRun(); // 监控模式:每 5 分钟推理一次,记录资源消耗,遇非全黑 out 退出
int ReJson_product(std::vector<Json_Det_Path> &jsonList, std::string strProductName); // 套图复测
int CheckFileImg(); ///< 检查文件图片(新增)
int CheckProduct(const LoadProductImages &product); ///< 检查单个产品(新增)
@ -799,6 +804,8 @@ public:
std::mutex m_checkNotifyMutex; // 通知队列锁
std::condition_variable m_checkNotifyCV; // 通知条件变量
std::atomic<int> m_nCheckNotifiedCount{0}; // 已通知数量
std::atomic<int> m_nProductSavedResultCount{0}; // 已保存结果数量(用于产品间同步等待)
std::condition_variable m_productResultCV; // 产品结果保存通知CV(每次save后notify)
std::set<std::string> m_productIdList; // 产品ID列表
};

@ -188,6 +188,14 @@ int main(int argc, char *argv[])
return 0;
}
}
else if (string(argv[i]) == "-testAIDet")
{
test.runConfig.det_Type = RUNTYPE_RUN_File_AI_DetTest;
if (argc == 3)
{
test.runConfig.filePath = string(argv[i + 1]);
}
}
else if (string(argv[i]) == "-feqx" && i + 1 < argc)
{
test.runConfig.det_Type = RUNTYPE_RUN_File_Edge_Test_Save_Qx;
@ -228,11 +236,20 @@ int main(int argc, char *argv[])
return 0;
}
}
else if (string(argv[i]) == "-tag" && i + 1 < argc)
{
test.runConfig.det_Type = RUNTYPE_RUN_File;
test.runConfig.filePath = string(argv[i + 1]);
test.runConfig.det_Tag = true;
}
if (string(argv[i]) == "-s")
{
test.runConfig.bDebugSaveImg = true;
}
if (string(argv[i]) == "-m")
{
test.runConfig.bmonitor = true;
}
if (string(argv[i]) == "-n" && i + 1 < argc)
{
std::string strnum = string(argv[i + 1]);
@ -245,10 +262,10 @@ int main(int argc, char *argv[])
signal(SIGINT, handler);
test.start();
while (true)
{
usleep(10 * 1000);
}
// while (true)
// {
// usleep(10 * 1000);
// }
return 0;
}

Loading…
Cancel
Save