Compare commits

...

60 Commits

Author SHA1 Message Date
liusiyang a0f772b209 feat 添加清晰度计算,卡控模糊图
6 days ago
liusiyang b404e97577 update 启用弱化区域
1 week ago
liusiyang 1d5c6d7c8d update 优化存图路径
1 week ago
liusiyang 7a5c913f2f update 优化pin脚投影矩阵计算
1 week ago
liusiyang aa5037e74d feat 添加pin脚投影方式
2 weeks ago
liusiyang 050dd31240 update 优化pin整体偏移计算方式
2 weeks ago
liusiyang 211296fc20 feat 添加参数配置pin角方法选择
2 weeks ago
liusiyang e5cd28f970 v1.3.1
2 weeks ago
liusiyang d3438a0815 update 优化pin整体偏移
2 weeks ago
liusiyang 28b36c70ff fix 只读卡口图片
2 weeks ago
liusiyang 5ad8f89815 fix 分类添加panel,分类分数不做softmax
2 weeks ago
liusiyang 13afe246d5 update 不仿射原图,对模板Pin进行仿射
2 weeks ago
liusiyang b7da6688ba feat Pin添加dxdy日志
3 weeks ago
liusiyang 3fabada875 feat 完善-f批量测试
3 weeks ago
liusiyang c50c55974a fix scale没应用界面参数
3 weeks ago
liusiyang 2ce4bf4f17 feat 添加混料判断
3 weeks ago
liusiyang fe9fdcd781 update 优化存图
3 weeks ago
liusiyang d4b37dc077 update 不区分黑白缺陷
3 weeks ago
liusiyang 5fcac6a4bf feat PIN_PianYi不受区域参数控制
3 weeks ago
liusiyang 232c6a3bbf v1.3.0
3 weeks ago
liusiyang d6c0abdc27 feat 开放分类映射配置文件
3 weeks ago
liusiyang f8999aeb34 feat 开放模型路径配置文件
3 weeks ago
liusiyang c7b20f2c75 update 整理AI分类列表
3 weeks ago
liusiyang d83ade76ca feat 添加QueXi模型开关
3 weeks ago
liusiyang ad4bc5cfc7 update 优化存图
3 weeks ago
liusiyang df101943fe v1.2.0
3 weeks ago
liusiyang c4ca12293e feat 添加直线拟合方式
3 weeks ago
liusiyang 3fb6f4ec4c update 优化缺陷列表2
3 weeks ago
liusiyang 871d14adb6 update 修改AI检测流程
3 weeks ago
liusiyang ba394569ec update 仿射变换使用内定位
3 weeks ago
liusiyang e23c3df9d5 update 优化缺陷列表1
3 weeks ago
liusiyang 41de73ee57 v1.1.0
3 weeks ago
liusiyang 8e411d84c6 update 完善缺陷列表映射
3 weeks ago
liusiyang 9a14079b61 feat 返回缺陷信息新增缺陷等级字段(待实现缺陷分级逻辑)
4 weeks ago
liusiyang 38480076e9 feat 传入图片对竖图旋转
4 weeks ago
liusiyang 4dd6cfe2a0 update 使用双显卡
4 weeks ago
liusiyang 5d92931141 update 原图也不改变通道,修复内定位绘制
4 weeks ago
liusiyang 40ba4cea63 update 内定位使用裁剪图
1 month ago
liusiyang 1bb4d3f277 update 结果图不修改为灰度图
1 month ago
liusiyang 65948e2eee update 修改读取参数,修复并发日志崩溃
1 month ago
liusiyang cbb7737c9a feat 区分残点来源,日志+存图
2 months ago
liusiyang 68f59a7a77 update 调整cpu核绑定
2 months ago
liusiyang 464954b898 feat 优化edge和blob耗时
2 months ago
liusiyang b1b6a29402 update 优化缺陷列表
2 months ago
liusiyang 455884c373 update 修改缺陷名称
2 months ago
liusiyang 85302b8fa9 feat 添加内框xy偏移
2 months ago
liusiyang 7460f61e26 update 更改pin偏移缺失判断条件
2 months ago
liusiyang 384fc6dd24 update 更新参数配置
2 months ago
liusiyang d0af74cf4e update 参数更新时再进行一次初始化模板
2 months ago
liusiyang 11d7e22d64 feat 添加缺锡模型
2 months ago
liusiyang 456b56e8d4 feat 使用单显卡,添加循环测试
2 months ago
liusiyang dcde7fd9e7 update 整理1
2 months ago
liusiyang 8d85316edb update 修改模型加载路径
2 months ago
liusiyang 4282c4253d feat 安装的头库文件添加后缀
2 months ago
xiewenji 18afe43979 update 添加异步存图
2 months ago
xiewenji fcc8fd70c7 update 初步偏移检测
3 months ago
xiewenji 37d050139c update 完善结果图绘制
3 months ago
xiewenji f0eb786864 update 整理Pin脚检测
3 months ago
xiewenji 3dba31a9b4 feat 模板参数传递,完成模板对齐
3 months ago
xiewenji 874a531076 feat 添加初始化模板参数
3 months ago

@ -12,6 +12,7 @@
#include <vector> #include <vector>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <atomic>
#include "NvInfer.h" #include "NvInfer.h"
#include "cuda_runtime_api.h" #include "cuda_runtime_api.h"
#include "AI_Factory.h" #include "AI_Factory.h"
@ -102,7 +103,8 @@ public:
} }
if (cpu_floatData != NULL) if (cpu_floatData != NULL)
{ {
delete cpu_floatData; // 与 LoadEngine 中的 new float[datalength] 配对,必须用 delete[]
delete[] cpu_floatData;
cpu_floatData = NULL; cpu_floatData = NULL;
} }
} }
@ -213,8 +215,8 @@ private:
int m_nALLStreamNum; int m_nALLStreamNum;
private: private:
// 上次使用的GPU stream Idx; // 上次使用的GPU stream Idx(多线程并发调用,需原子自增避免竞争);
int m_nLast_GPUStreamIdx; std::atomic<int> m_nLast_GPUStreamIdx;
}; };
#endif #endif

@ -154,6 +154,7 @@ public:
private: private:
public: public:
std::shared_ptr<AIModel_Base> Defect; std::shared_ptr<AIModel_Base> Defect;
std::shared_ptr<AIModel_Base> Defect_QueXi;
std::shared_ptr<AIModel_Base> Class; std::shared_ptr<AIModel_Base> Class;
std::shared_ptr<AIModel_Base> Pin_Loc; std::shared_ptr<AIModel_Base> Pin_Loc;

@ -20,7 +20,7 @@ AIModel_Impl::AIModel_Impl()
m_pNode_output_1 = NULL; m_pNode_output_1 = NULL;
m_pNode_output_2 = NULL; m_pNode_output_2 = NULL;
m_DetGPUStream.clear(); m_DetGPUStream.clear();
m_nLast_GPUStreamIdx = 0; m_nLast_GPUStreamIdx.store(0);
m_nALLStreamNum = 0; m_nALLStreamNum = 0;
} }
AIModel_Impl::~AIModel_Impl() AIModel_Impl::~AIModel_Impl()
@ -169,6 +169,19 @@ int AIModel_Impl::Init(AIModelRun_Config config)
} }
m_bInitSuccess = true; m_bInitSuccess = true;
// warm-up:首次推理会触发 TensorRT/CUDA 懒初始化(kernel 加载、workspace 分配、首次 enqueueV3 建图等),
// 提前用一张空图预热,避免首张检测图耗时飙升。
{
cv::Mat warmIn = InitMat(m_pNode_input_0->channel, m_pNode_input_0->width, m_pNode_input_0->height);
cv::Mat warmOut;
int warmRe = AIDet(warmIn, warmOut);
if (warmRe != 0)
{
printf("%s warm-up error = %d\n", m_modelRun_Config.strName.c_str(), warmRe);
}
}
return 0; return 0;
} }
cv::Mat AIModel_Impl::InitMat(int channel, int w, int h) cv::Mat AIModel_Impl::InitMat(int channel, int w, int h)
@ -324,7 +337,8 @@ int AIModel_Impl::LoadEngine(int ngpuIdx)
{ {
if (steamNodeconfig->cpu_floatData) if (steamNodeconfig->cpu_floatData)
{ {
delete steamNodeconfig->cpu_floatData; // 与下面 new float[datalength] 配对,必须用 delete[]
delete[] steamNodeconfig->cpu_floatData;
steamNodeconfig->cpu_floatData = NULL; steamNodeconfig->cpu_floatData = NULL;
} }
@ -479,7 +493,11 @@ int AIModel_Impl::AI_Det_In_1_Out_1(Node_Config *pConfig_in, Node_Config *pConfi
{ {
// printf("=== s1 "); // printf("=== s1 ");
std::shared_ptr<Det_GPU_Stram> pdetStream; std::shared_ptr<Det_GPU_Stram> pdetStream;
GetStream(pdetStream); if (GetStream(pdetStream) != 0 || !pdetStream)
{
printf("AI_Det_In_1_Out_1: GetStream error \n");
return 3;
}
// printf("=== s2 "); // printf("=== s2 ");
std::lock_guard<std::mutex> lock(pdetStream->AI_mutex); std::lock_guard<std::mutex> lock(pdetStream->AI_mutex);
// printf(" ss g %d s %d -- ", pdetStream->nGPUIdx, pdetStream->cuda_stream->nstreamIdx); // printf(" ss g %d s %d -- ", pdetStream->nGPUIdx, pdetStream->cuda_stream->nstreamIdx);
@ -508,22 +526,40 @@ int AIModel_Impl::AI_Det_In_1_Out_1(Node_Config *pConfig_in, Node_Config *pConfi
int AIModel_Impl::GetStream(std::shared_ptr<Det_GPU_Stram> &pdetStream) int AIModel_Impl::GetStream(std::shared_ptr<Det_GPU_Stram> &pdetStream)
{ {
int sidx = m_nLast_GPUStreamIdx; if (m_nALLStreamNum <= 0 || m_DetGPUStream.empty())
{
return 1;
}
sidx++; // 多线程并发时原子轮询分配 stream:
if (sidx >= m_nALLStreamNum) int sidx = m_nLast_GPUStreamIdx.load();
while (true)
{ {
sidx = 0; int next = sidx + 1;
if (next >= m_nALLStreamNum)
{
next = 0;
}
if (m_nLast_GPUStreamIdx.compare_exchange_weak(sidx, next))
{
sidx = next;
break;
}
// CAS 失败时 sidx 已被更新为当前实际值,重新计算 next
} }
pdetStream = m_DetGPUStream.at(sidx);
m_nLast_GPUStreamIdx = sidx;
pdetStream = m_DetGPUStream.at(sidx);
return 0; return 0;
} }
int AIModel_Impl::AI_Det_In_1_Out_1_class(unsigned char *p_indata_0, float *fmaxScore) int AIModel_Impl::AI_Det_In_1_Out_1_class(unsigned char *p_indata_0, float *fmaxScore)
{ {
std::shared_ptr<Det_GPU_Stram> pdetStream; std::shared_ptr<Det_GPU_Stram> pdetStream;
GetStream(pdetStream); if (GetStream(pdetStream) != 0 || !pdetStream)
{
printf("AI_Det_In_1_Out_1_class: GetStream error \n");
*fmaxScore = 0.0f;
return -3;
}
// printf("=== s2 "); // printf("=== s2 ");
std::lock_guard<std::mutex> lock(pdetStream->AI_mutex); std::lock_guard<std::mutex> lock(pdetStream->AI_mutex);
// printf(" ss g %d s %d -- ", pdetStream->nGPUIdx, pdetStream->cuda_stream->nstreamIdx); // printf(" ss g %d s %d -- ", pdetStream->nGPUIdx, pdetStream->cuda_stream->nstreamIdx);
@ -564,8 +600,8 @@ int AIModel_Impl::F2softmaxId(float *data, int class_num, float *fmaxScore)
std::vector<float> output(cls_num); std::vector<float> output(cls_num);
for (int i = 0; i < output.size(); i++) for (int i = 0; i < output.size(); i++)
{ {
float outi = (float)exp(data[i]) / (float)total; // float outi = (float)exp(data[i]) / (float)total;
output[i] = outi; output[i] = data[i]; // 不做softmax处理(模型已经做了)
// output.push_back(outi); // output.push_back(outi);
// std::cout<<"output["<<i<<"]="<<output[i]<<std::endl; // std::cout<<"output["<<i<<"]="<<output[i]<<std::endl;
} }

@ -10,6 +10,71 @@
#include "AIModel_Impl.h" #include "AIModel_Impl.h"
#include <fstream>
#include <map>
// 模型路径配置文件(键值对格式,示例:"Defect":/home/aidlux/BOE/UseModel_ZB/KK/Defect.engine)
static const std::string MODEL_PATH_CONFIG_FILE = "/home/aidlux/BOE/UseModel_ZB/KK.txt";
// 去掉字符串首尾的空白字符和引号
static std::string TrimStr(const std::string &str)
{
size_t begin = str.find_first_not_of(" \t\r\n\"");
if (begin == std::string::npos)
{
return "";
}
size_t end = str.find_last_not_of(" \t\r\n\"");
return str.substr(begin, end - begin + 1);
}
// 读取模型路径键值对配置文件(每行格式:模型名:模型路径,支持 "模型名":模型路径 以及 # 注释行)
static std::map<std::string, std::string> ReadModelPathConfig(const std::string &strFilePath)
{
std::map<std::string, std::string> mapConfig;
std::ifstream ifs(strFilePath.c_str());
if (!ifs.is_open())
{
printf("Open model config file failed, use default paths: %s\n", strFilePath.c_str());
return mapConfig;
}
std::string strLine;
while (std::getline(ifs, strLine))
{
// 跳过空行和注释行
std::string strTrim = TrimStr(strLine);
if (strTrim.empty() || strTrim[0] == '#')
{
continue;
}
size_t pos = strLine.find(':');
if (pos == std::string::npos)
{
continue;
}
std::string strKey = TrimStr(strLine.substr(0, pos));
std::string strValue = TrimStr(strLine.substr(pos + 1));
if (!strKey.empty() && !strValue.empty())
{
mapConfig[strKey] = strValue;
}
}
return mapConfig;
}
// 优先从配置中取模型路径,配置中没有则使用默认路径
static std::string GetModelPath(const std::map<std::string, std::string> &mapConfig,
const std::string &strKey, const std::string &strDefaultPath)
{
std::map<std::string, std::string>::const_iterator it = mapConfig.find(strKey);
if (it != mapConfig.end())
{
return it->second;
}
return strDefaultPath;
}
std::shared_ptr<AIModel_Base> AIModel_Base::GetInstance() std::shared_ptr<AIModel_Base> AIModel_Base::GetInstance()
{ {
std::shared_ptr<AIModel_Base> instance = std::make_shared<AIModel_Impl>(); std::shared_ptr<AIModel_Base> instance = std::make_shared<AIModel_Impl>();
@ -37,25 +102,39 @@ int AIFactory::InitALLAIModle(GPU_Config gupconfig)
return 1; return 1;
} }
// 读取模型路径配置文件,配置文件中有的路径优先,没有的用默认路径
std::map<std::string, std::string> mapModelPaths = ReadModelPathConfig(MODEL_PATH_CONFIG_FILE);
if (!Defect) if (!Defect)
{ {
Defect = AIModel_Base::GetInstance(); Defect = AIModel_Base::GetInstance();
AIModel_Base::AIModelRun_Config edge_config; AIModel_Base::AIModelRun_Config edge_config;
edge_config.gpuconfig.copy(gupconfig); edge_config.gpuconfig.copy(gupconfig);
edge_config.strPath = "/home/aidlux/BOE/UseModel_ZB/Defect.engine"; edge_config.strPath = GetModelPath(mapModelPaths, "Defect", "/home/aidlux/BOE/UseModel_ZB/KK/Defect.engine");
edge_config.strName = "CA_Det"; edge_config.strName = "Det";
edge_config.inputType = AIModel_Base::Input_HWC; edge_config.inputType = AIModel_Base::Input_CHW;
edge_config.Stream_num = 2; edge_config.Stream_num = 2;
Defect->Init(edge_config); Defect->Init(edge_config);
} }
if (!Defect_QueXi)
{
Defect_QueXi = AIModel_Base::GetInstance();
AIModel_Base::AIModelRun_Config edge_config;
edge_config.gpuconfig.copy(gupconfig);
edge_config.strPath = GetModelPath(mapModelPaths, "QueXi", "/home/aidlux/BOE/UseModel_ZB/KK/QueXi.engine");
edge_config.strName = "QueXi_Det";
edge_config.inputType = AIModel_Base::Input_CHW;
edge_config.Stream_num = 2;
Defect_QueXi->Init(edge_config);
}
if (!Class) if (!Class)
{ {
Class = AIModel_Base::GetInstance(); Class = AIModel_Base::GetInstance();
AIModel_Base::AIModelRun_Config jbl_config; AIModel_Base::AIModelRun_Config jbl_config;
jbl_config.gpuconfig.copy(gupconfig); jbl_config.gpuconfig.copy(gupconfig);
jbl_config.strPath = "/home/aidlux/BOE/UseModel_ZB/Class_10.engine"; jbl_config.strPath = GetModelPath(mapModelPaths, "Class", "/home/aidlux/BOE/UseModel_ZB/KK/Class.engine");
jbl_config.inputType = AIModel_Base::Input_HWC; jbl_config.inputType = AIModel_Base::Input_CHW;
jbl_config.strName = "CA_Class"; jbl_config.strName = "Class";
jbl_config.IsClass = true; jbl_config.IsClass = true;
Class->Init(jbl_config); Class->Init(jbl_config);
} }
@ -64,9 +143,9 @@ int AIFactory::InitALLAIModle(GPU_Config gupconfig)
Pin_Loc = AIModel_Base::GetInstance(); Pin_Loc = AIModel_Base::GetInstance();
AIModel_Base::AIModelRun_Config edge_config; AIModel_Base::AIModelRun_Config edge_config;
edge_config.gpuconfig.copy(gupconfig); edge_config.gpuconfig.copy(gupconfig);
edge_config.strPath = "/home/aidlux/BOE/UseModel_ZB/PinLoc.engine"; edge_config.strPath = GetModelPath(mapModelPaths, "PinLoc", "/home/aidlux/BOE/UseModel_ZB/KK/PinLoc.engine");
edge_config.strName = "TA_Det"; edge_config.strName = "Pin_Loc";
edge_config.inputType = AIModel_Base::Input_HWC; edge_config.inputType = AIModel_Base::Input_CHW;
edge_config.Stream_num = 2; edge_config.Stream_num = 2;
Pin_Loc->Init(edge_config); Pin_Loc->Init(edge_config);
} }
@ -75,9 +154,10 @@ int AIFactory::InitALLAIModle(GPU_Config gupconfig)
Align_Outer = AIModel_Base::GetInstance(); Align_Outer = AIModel_Base::GetInstance();
AIModel_Base::AIModelRun_Config edge_config; AIModel_Base::AIModelRun_Config edge_config;
edge_config.gpuconfig.copy(gupconfig); edge_config.gpuconfig.copy(gupconfig);
edge_config.strPath = "/home/aidlux/BOE/UseModel_ZB/AlignOuter.engine"; edge_config.strPath = GetModelPath(mapModelPaths, "AlignOuter", "/home/aidlux/BOE/UseModel_ZB/KK/AlignOuter.engine");
edge_config.strName = "Align"; edge_config.strName = "Align_Outer";
edge_config.inputType = AIModel_Base::Input_HWC; edge_config.inputType = AIModel_Base::Input_CHW;
edge_config.Stream_num = 2;
Align_Outer->Init(edge_config); Align_Outer->Init(edge_config);
} }
if (!Align_Inner) if (!Align_Inner)
@ -85,9 +165,10 @@ int AIFactory::InitALLAIModle(GPU_Config gupconfig)
Align_Inner = AIModel_Base::GetInstance(); Align_Inner = AIModel_Base::GetInstance();
AIModel_Base::AIModelRun_Config edge_config; AIModel_Base::AIModelRun_Config edge_config;
edge_config.gpuconfig.copy(gupconfig); edge_config.gpuconfig.copy(gupconfig);
edge_config.strPath = "/home/aidlux/BOE/UseModel_ZB/AlignInner.engine"; edge_config.strPath = GetModelPath(mapModelPaths, "AlignInner", "/home/aidlux/BOE/UseModel_ZB/KK/AlignInner.engine");
edge_config.strName = "Mark"; edge_config.strName = "Align_Inner";
edge_config.inputType = AIModel_Base::Input_HWC; edge_config.inputType = AIModel_Base::Input_CHW;
edge_config.Stream_num = 2;
Align_Inner->Init(edge_config); Align_Inner->Init(edge_config);
} }
m_bInitSucc = true; m_bInitSucc = true;

@ -78,11 +78,14 @@ set(ModuleName "")
# 自定义安装前缀 # 自定义安装前缀
set(CMAKE_INSTALL_PREFIX /usr/local/zb_socket CACHE PATH "Install path prefix" FORCE) set(CMAKE_INSTALL_PREFIX /usr/local/zb_socket CACHE PATH "Install path prefix" FORCE)
set(HEADER_FILES include/ImgCheckBase.h include/ImgCheckConfig.h) set(HEADER_FILES include/ImgCheckBase.h include/ImgCheckConfig.h)
# 库文件添加 _socket 后缀
set_target_properties(TY_Check PROPERTIES OUTPUT_NAME "TY_Check_socket")
# 安装动态库 # 安装动态库
install(TARGETS TY_Check install(TARGETS TY_Check
LIBRARY DESTINATION lib # 安装到 CMAKE_INSTALL_PREFIX/lib LIBRARY DESTINATION lib # 安装到 CMAKE_INSTALL_PREFIX/lib
ARCHIVE DESTINATION lib/static ARCHIVE DESTINATION lib/static
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
PUBLIC_HEADER DESTINATION include) # 安装到 CMAKE_INSTALL_PREFIX/include PUBLIC_HEADER DESTINATION include) # 安装到 CMAKE_INSTALL_PREFIX/include
# 安装头文件 # 安装头文件(添加 _socket 后缀)
install(FILES ${HEADER_FILES} DESTINATION include) install(FILES include/ImgCheckBase.h DESTINATION include RENAME ImgCheckBase_socket.h)
install(FILES include/ImgCheckConfig.h DESTINATION include RENAME ImgCheckConfig_socket.h)

@ -13,31 +13,27 @@
enum AI_CLass_QX_NAME_ enum AI_CLass_QX_NAME_
{ {
AI_CLass_QX_NAME_aotudian, // 凹凸点 AI_CLass_QX_NAME_PIN_PoSun, // PIN破损
AI_CLass_QX_NAME_other, // 其他 AI_CLass_QX_NAME_PIN_QueShi, // PIN缺失
AI_CLass_QX_NAME_line, // 线状 AI_CLass_QX_NAME_BTB_DuanLie, // BTB断裂
AI_CLass_QX_NAME_zangwu, // 脏污 AI_CLass_QX_NAME_LianXi, // 连锡
AI_CLass_QX_NAME_dianzhuang, // 点状 AI_CLass_QX_NAME_QueXi, // 缺锡
AI_CLass_QX_NAME_posun, // 破损 AI_CLass_QX_NAME_YiWu, // 异物
AI_CLass_QX_NAME_xianwei, // 纤维 AI_CLass_QX_NAME_C_BianXing, // C型卡口异常
AI_CLass_QX_NAME_shuizi, // 水渍 AI_CLass_QX_NAME_Panel, // 其他
AI_CLass_QX_NAME_danban, // 淡斑
AI_CLass_QX_NAME_fuchen, // 浮尘
AI_CLass_QX_NAME_count, AI_CLass_QX_NAME_count,
}; };
// 缺陷项对应在参数中的名称 // 缺陷项对应在参数中的名称
static const std::string AI_CLass_QX_NAME_Names[] = static const std::string AI_CLass_QX_NAME_Names[] =
{ {
"aotudian", "PIN_PoSun",
"qx", "PIN_QueShi",
"line", "BTB_DuanLie",
"zangwu", "LianXi",
"dianzhuang", "QueXi",
"posun", "YiWu",
"xianwei", "C_BianXing",
"shuizi", "Panel",
"danban",
"fuchen",
}; };
#endif #endif

@ -67,7 +67,7 @@ public:
} }
void Init() void Init()
{ {
buseOfft = false; buseOfft = true;
nresult = 0; nresult = 0;
offt_x = 0; offt_x = 0;
offt_y = 0; offt_y = 0;
@ -94,6 +94,7 @@ public:
{ {
int ncamId; // 相机ID int ncamId; // 相机ID
BaseCheckFunction *pBaseCheckFunction; BaseCheckFunction *pBaseCheckFunction;
std::string strProductID; // 产品ID(存图文件名用)
std::string strChannel; // 通道 std::string strChannel; // 通道
int nthresholdvalue; // 背景阈值 int nthresholdvalue; // 背景阈值
int nAIErodesize; // 边缘腐蚀强度 int nAIErodesize; // 边缘腐蚀强度
@ -116,6 +117,7 @@ public:
saveProcessImg = Save_Close; saveProcessImg = Save_Close;
bUseDrawRoi_Check = false; bUseDrawRoi_Check = false;
drawRoi = cv::Rect(0, 0, 0, 0); drawRoi = cv::Rect(0, 0, 0, 0);
strProductID = "";
if (!drawMask.empty()) if (!drawMask.empty())
{ {
drawMask.release(); drawMask.release();

@ -65,6 +65,13 @@ public:
std::string GetErrorInfo(); std::string GetErrorInfo();
// 获取模板外接矩形
const cv::RotatedRect &GetTplOuterRect() const { return m_tplOuterRect; }
// 获取模板内边缘矩形
const cv::RotatedRect &GetTplInnerRect() const { return m_tplInnerRect; }
// 获取模板 pin blob 轮廓点集
const std::vector<std::vector<cv::Point>> &GetTplPinContours() const { return m_tplPinContours; }
private: private:
// 加载运行参数 // 加载运行参数
int LoadRunConfig(void *p); int LoadRunConfig(void *p);
@ -125,6 +132,9 @@ private:
int InitAIFactory(); int InitAIFactory();
// 初始化模板
int InitTemplateMask();
private: private:
// 图片处理线程 // 图片处理线程
std::shared_ptr<std::thread> ptr_thread_Run; std::shared_ptr<std::thread> ptr_thread_Run;
@ -170,6 +180,10 @@ private:
std::string m_strTest; std::string m_strTest;
// 模板信息
cv::RotatedRect m_tplOuterRect; // 模板外接矩形
cv::RotatedRect m_tplInnerRect; // 模板内边缘矩形
std::vector<std::vector<cv::Point>> m_tplPinContours; // 模板 pin blob 轮廓点集
}; };

@ -133,6 +133,11 @@ public:
int Init(std::string strcameraName); int Init(std::string strcameraName);
int StartCheck(std::shared_ptr<CameraResult> pCamera_Check_Result); int StartCheck(std::shared_ptr<CameraResult> pCamera_Check_Result);
// 设置模板信息并转发给各个检测核心
void SetTplInfo(const cv::RotatedRect &outerRect,
const cv::RotatedRect &innerRect,
const std::vector<std::vector<cv::Point>> &pinContours);
private: private:
// 开启线程 // 开启线程
int StartThread(); int StartThread();

@ -33,6 +33,10 @@ public:
static std::string getCurTimeHMS(); static std::string getCurTimeHMS();
static std::string Op_float2String(float nvalue); static std::string Op_float2String(float nvalue);
static int64_t getSnowId(); static int64_t getSnowId();
// 当前日期(年月日, 8 位纯数字), 形如 "20261029", 便于存图文件名检索/排序
static std::string getCurrentDateYMD();
// 生成不重名的存图完整路径 = strDir + strPrefix + [ "_序号" ] + "_" + strName + strExt
static std::string GetUniqueImgPath(const std::string &strDir, const std::string &strPrefix, const std::string &strName, const std::string &strExt);
static bool JudgRect(cv::Rect roi, int img_w, int img_h); static bool JudgRect(cv::Rect roi, int img_w, int img_h);
static bool JudgRect_SZ(cv::Rect roi, int w, int h); static bool JudgRect_SZ(cv::Rect roi, int w, int h);
static bool compareIgnoreCase(const std::string &str1, const std::string &str2); static bool compareIgnoreCase(const std::string &str1, const std::string &str2);
@ -51,6 +55,9 @@ public:
static float Cal2PointAngle(cv::Point p_left, cv::Point p_right); static float Cal2PointAngle(cv::Point p_left, cv::Point p_right);
// 获取 RotatedRect 长轴的方向角(归一化到 [0, 180) 度) // 获取 RotatedRect 长轴的方向角(归一化到 [0, 180) 度)
static float getLongAxisAngle(const cv::RotatedRect rect); static float getLongAxisAngle(const cv::RotatedRect rect);
// 排序RotatedRect的四个顶点
static std::vector<cv::Point2f> sort_vertices(cv::RotatedRect rrect);
// 找图片的最大外轮廓 // 找图片的最大外轮廓
static std::vector<cv::Point> getLargestContourROI(const cv::Mat &binaryImg, bool &found); static std::vector<cv::Point> getLargestContourROI(const cv::Mat &binaryImg, bool &found);
@ -59,12 +66,32 @@ public:
//创建目录 //创建目录
static int CreateDir(const std::string &dir); static int CreateDir(const std::string &dir);
// 异步存图
static int SaveImgAsync(const std::string &path, const cv::Mat &img);
// 点的距离是否过小 // 点的距离是否过小
static bool bcalDis(cv::Point p1, cv::Point p2, int disT); static bool bcalDis(cv::Point p1, cv::Point p2, int disT);
static double calDis(cv::Point p1, cv::Point p2); static double calDis(cv::Point p1, cv::Point p2);
static void PrintRect(cv::Rect roi, std::string str = ""); static void PrintRect(cv::Rect roi, std::string str = "");
static int cutSmallImg(cv::Mat img, std::vector<cv::Rect> &samllRoiList, cv::Rect config_roi, int config_SmallImg_Width, int config_SmallImg_Height, int config_MinOverlap_Width, int config_MinOverlap_Height); static int cutSmallImg(cv::Mat img, std::vector<cv::Rect> &samllRoiList, cv::Rect config_roi, int config_SmallImg_Width, int config_SmallImg_Height, int config_MinOverlap_Width, int config_MinOverlap_Height);
static cv::Point2f transformPoint(const cv::Point2f &point, const cv::Mat &transform_matrix); static cv::Point2f transformPoint(const cv::Point2f &point, const cv::Mat &transform_matrix);
// 依目标图像通道数返回可用的绘制颜色:
// 1 通道(灰度) -> 统一白色,保证可见
// 4 通道(BGRA) -> 补 alpha = 255
// 3 通道 -> 原样返回
static inline cv::Scalar GetDrawColor(const cv::Mat &img, const cv::Scalar &color)
{
if (img.channels() == 1)
{
return cv::Scalar(255);
}
if (img.channels() == 4)
{
return cv::Scalar(color[0], color[1], color[2], 255);
}
return color;
}
}; };
// 无参数重载:直接返回原字符串,避免 -Wformat-security 警告 // 无参数重载:直接返回原字符串,避免 -Wformat-security 警告
static inline std::string str_Format(const std::string &format) static inline std::string str_Format(const std::string &format)

@ -13,7 +13,13 @@
// 检测分析线程类数目 // 检测分析线程类数目
#define IMGCHECKANALYSISY_NUM 2 #define IMGCHECKANALYSISY_NUM 2
// 检测线程绑定的 CPU 核带(核带下沉到库内部,调用方传入的 nCpu_start_Idx / nCpu_num 不再生效)
// 分配规则:第 k 个检测线程绑定 核 = CHECK_CPU_BAND_START + (k % CHECK_CPU_BAND_NUM)
// 当前 24-27 = NUMA node1 的物理核 8-11(逻辑核同物理核映射:c 与 c+16 为超线程兄弟)
// 与 BOE_ZB_BACKPLATE(16-19 / node0)零重叠,两进程并行时互不干扰
// 注意:CHECK_CPU_BAND_NUM 必须 >= 相机数 * IMGCHECKANALYSISY_NUM,否则取模回绕会导致两个检测线程绑同一核
#define CHECK_CPU_BAND_START 24
#define CHECK_CPU_BAND_NUM 8
// 相机ID 的相关定义 // 相机ID 的相关定义
enum Camera_IDX enum Camera_IDX

@ -14,6 +14,7 @@
#include <string.h> #include <string.h>
#include <sys/time.h> #include <sys/time.h>
#include <vector> #include <vector>
#include <mutex>
#include "ImgCheckConfig.h" #include "ImgCheckConfig.h"
#include "CheckErrorCodeDefine.hpp" #include "CheckErrorCodeDefine.hpp"
using namespace std; using namespace std;
@ -61,6 +62,9 @@ struct DetLog
std::vector<std::string> logList; std::vector<std::string> logList;
bool bPrintStr; // 是否打印日志 bool bPrintStr; // 是否打印日志
int addLogLevel; int addLogLevel;
// 保护 logList:同一个 DetLog(如图片结果的日志)会被实例主线程(Run/CheckRun)
// 与任务线程(AI 推理/分类) 并发写入,无锁 push_back 会造成 vector 堆破坏。
std::mutex mtx_log;
DetLog() DetLog()
{ {
@ -68,11 +72,19 @@ struct DetLog
} }
void Init() void Init()
{ {
std::lock_guard<std::mutex> lock(mtx_log);
logList.clear(); logList.clear();
bPrintStr = false; bPrintStr = false;
addLogLevel = DET_LOG_LEVEL_3; addLogLevel = DET_LOG_LEVEL_3;
} }
// 线程安全地获取日志快照
std::vector<std::string> GetLogs()
{
std::lock_guard<std::mutex> lock(mtx_log);
return logList;
}
template <typename... Args> template <typename... Args>
void AddCheckstr(PrintLevel_ step, std::string stepStr, const std::string &format, Args... args) void AddCheckstr(PrintLevel_ step, std::string stepStr, const std::string &format, Args... args)
{ {
@ -105,6 +117,7 @@ struct DetLog
printf("%s\n", str.c_str()); printf("%s\n", str.c_str());
} }
std::lock_guard<std::mutex> lock(mtx_log);
logList.push_back(str); logList.push_back(str);
} }
template <typename... Args> template <typename... Args>
@ -141,15 +154,19 @@ struct DetLog
if (LogLevel <= addLogLevel) if (LogLevel <= addLogLevel)
{ {
std::lock_guard<std::mutex> lock(mtx_log);
logList.push_back(str); logList.push_back(str);
} }
} }
void printLog(std::string str) void printLog(std::string str)
{ {
printf("===========================%s==============================\n", str.c_str()); printf("===========================%s==============================\n", str.c_str());
for (auto strl : logList)
{ {
printf("%s\n", strl.c_str()); std::lock_guard<std::mutex> lock(mtx_log);
for (auto strl : logList)
{
printf("%s\n", strl.c_str());
}
} }
printf("==========================================================\n"); printf("==========================================================\n");
} }

@ -44,6 +44,13 @@ public:
cv::Mat AI_mask; cv::Mat AI_mask;
}; };
// 单个检测模型输出的 全图 mask,用于追溯 残点(blob) 由哪个模型检出
struct AI_Model_MaskImg
{
std::string strModelName; // 模型名称
cv::Mat ModelMask; // 该模型输出的 全图 mask
};
private: private:
/* data */ /* data */
public: public:
@ -87,6 +94,8 @@ public:
std::shared_ptr<CameraBaseResult> cameraBaseResult; // 相机相关的基本结果信息 std::shared_ptr<CameraBaseResult> cameraBaseResult; // 相机相关的基本结果信息
std::vector<std::shared_ptr<AI_Det_MaskImg>> AI_Qx_MaskList; std::vector<std::shared_ptr<AI_Det_MaskImg>> AI_Qx_MaskList;
// 各检测模型独立的全图 mask 列表(与检测模型一一对应)
std::vector<std::shared_ptr<AI_Model_MaskImg>> AI_Model_MaskList;
std::mutex mtx_Det; // 互斥量 std::mutex mtx_Det; // 互斥量
private: private:
PRINT_LOG_ m_PrintLog; PRINT_LOG_ m_PrintLog;

@ -3,7 +3,7 @@
* @Date: 2025-09-11 15:32:52 * @Date: 2025-09-11 15:32:52
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-09-14 17:41:14 * @LastEditTime: 2025-09-14 17:41:14
* @FilePath: /BOE_CELL_AOI_Detect/AlgorithmModule/include/ImageDetBase.h * @FilePath: /BOE_ZB_SOCKET_Detect/AlgorithmModule/include/ImageDetBase.h
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
#ifndef ImageDetBase_H_ #ifndef ImageDetBase_H_

@ -27,6 +27,8 @@ struct QX_ERROR_INFO_
float fbreadth; float fbreadth;
int nconfig_qx_type; int nconfig_qx_type;
std::string qx_name; std::string qx_name;
std::string qx_level; // 缺陷等级
std::string strModelName; // 检出该残点(blob)的模型名称
int maxValue; int maxValue;
int whiteOrBlack; int whiteOrBlack;
float grayDis; float grayDis;
@ -53,6 +55,8 @@ struct QX_ERROR_INFO_
flen = 0; flen = 0;
nconfig_qx_type = 0; nconfig_qx_type = 0;
qx_name = ""; qx_name = "";
qx_level = "";
strModelName = "";
maxValue = 0; maxValue = 0;
grayDis = 0; grayDis = 0;
fUpIou = 0; fUpIou = 0;
@ -79,6 +83,8 @@ struct QX_ERROR_INFO_
<< ", Flen: " << flen << ", Flen: " << flen
<< ", nconfig_qx_type: " << nconfig_qx_type << ", nconfig_qx_type: " << nconfig_qx_type
<< ", QX Name: " << qx_name << ", QX Name: " << qx_name
<< ", QX Level: " << qx_level
<< ", Model Name: " << strModelName
<< ", Max Value: " << maxValue << ", Max Value: " << maxValue
<< ", Gray Dis: " << grayDis << ", Gray Dis: " << grayDis
<< ", fUpIou: " << fUpIou << ", fUpIou: " << fUpIou
@ -92,6 +98,18 @@ struct One_Image_CheckResult_
cv::Rect CutRoi; // 当前检测图片 对应的裁切区域 cv::Rect CutRoi; // 当前检测图片 对应的裁切区域
cv::Rect Param_CropRoi; // 参数模版 对应的裁切区域 cv::Rect Param_CropRoi; // 参数模版 对应的裁切区域
std::shared_ptr<std::vector<QX_ERROR_INFO_>> pQx_ErrorList; // 缺陷错误信息 std::shared_ptr<std::vector<QX_ERROR_INFO_>> pQx_ErrorList; // 缺陷错误信息
bool bBlurNG; // 图像模糊判定: true = 检测图像模糊(判 NG)
float fBlurScore; // 图像清晰度(拉普拉斯方差), 越大越清晰
One_Image_CheckResult_()
{
CutRoi = cv::Rect(0, 0, 0, 0);
Param_CropRoi = cv::Rect(0, 0, 0, 0);
pQx_ErrorList = std::make_shared<std::vector<QX_ERROR_INFO_>>();
bBlurNG = false;
fBlurScore = 0.0f;
}
void print(std::string str = "") void print(std::string str = "")
{ {
std::cout << str << ": " std::cout << str << ": "

@ -203,6 +203,8 @@ private:
std::condition_variable m_task_cv_; std::condition_variable m_task_cv_;
std::shared_ptr<std::thread> ptr_thread_Draw; std::shared_ptr<std::thread> ptr_thread_Draw;
std::condition_variable m_drawComplate_cv_; std::condition_variable m_drawComplate_cv_;
// 正在执行中的绘制任务数(受 m_task_mutex_ 保护),用于 WaiteDrawComplate 精确等待
int m_nDrawProcessingNum = 0;
}; };
#endif #endif

@ -80,6 +80,11 @@ public:
int creatsavedir(); int creatsavedir();
// 设置模板信息(模板外接矩形 + 模板内边缘矩形 + 模板 pin blob 轮廓点集)
void SetTplInfo(const cv::RotatedRect &outerRect,
const cv::RotatedRect &innerRect,
const std::vector<std::vector<cv::Point>> &pinContours);
private: private:
// 加载运行参数 // 加载运行参数
int LoadRunConfig(void *p); int LoadRunConfig(void *p);
@ -120,6 +125,9 @@ private:
// 图片预处理 // 图片预处理
int ImgPreDet(); int ImgPreDet();
// 图像模糊度(清晰度)检测: 在 AI 检测之前执行, 返回 1 表示图像模糊(判 NG), 0 正常
int BlurCheck(const cv::Mat &img);
// 设置新的检测参数 // 设置新的检测参数
int SetNewConfig(); int SetNewConfig();
@ -191,11 +199,32 @@ private:
// 边缘缺陷检测 // 边缘缺陷检测
int Pin_Qx_Det(const cv::Mat &img); int Pin_Qx_Det(const cv::Mat &img);
// 当前 Pin 检测方案是否为"直接分析"(参数 pinDet.Det_Method == "line_fitting")
bool IsDirectPinMethod() const;
// 当前 Pin 检测方案是否为"Pin 投影矩阵"(参数 pinDet.Det_Method == "Pin_Matrix")
bool IsPinMatrixMethod() const;
// 新方案: 不做模板对比, 只分析检测Pin: 自动分两排(考虑旋转) -> 拟合直线判偏移 + 相邻间距判缺失
int Pin_Direct_Check();
// Pin_Matrix 方案: 用当前帧 Pin 拟合投影矩阵(上下两排分别计算相似变换), 把模板 Pin 投影到当前检测图坐标系
int ComputePinProjectionMats();
// Pin_Matrix 方案: 过滤当前帧上明显的误检 pin blob(按模板 pin 的包围盒尺寸)
int FilterCurPinBlobsByTplSize();
// 把blob 汇总成 检测结果。 // 把blob 汇总成 检测结果。
int BLobToDetResult(); int BLobToDetResult();
//自适应参数区域更新 // 获取指定 残点(blob) 是由哪个(哪些)检测模型检出的
int Adapt_Config(Mat img, Rect roi, bool b_update); std::string GetBlobModelName(const cv::Rect &roi);
// 计算 当前图 -> 模板图 / 模板图 -> 当前图 的仿射矩阵(基准矩形可为内定位/外定位)
// 注意: 不再对原图做 warpAffine, 只做坐标变换
int CalAffineMats(const cv::RotatedRect &tplRefRect, const cv::RotatedRect &curRefRect);
// 把模板 Pin 轮廓(pinImg 裁剪坐标系)反变换到当前检测图(detImg)坐标系
int TransformTplPinContoursToCur();
std::shared_ptr<AIFactory> AI_Factory; std::shared_ptr<AIFactory> AI_Factory;
@ -262,8 +291,7 @@ private:
std::vector<cv::Rect> SmallRoiList; std::vector<cv::Rect> SmallRoiList;
std::string m_strRootPath_TA_cls; std::string m_strRootPath_KK_cls;
std::string m_strRootPath_CA_cls;
std::string m_strSavePath_cls; std::string m_strSavePath_cls;
std::string m_strLastDate; std::string m_strLastDate;
@ -287,6 +315,20 @@ private:
cv::Point m_old_cur_markLine_mark1; cv::Point m_old_cur_markLine_mark1;
cv::Point m_old_cur_markLine_mark2; cv::Point m_old_cur_markLine_mark2;
std::vector<RegionConfigST> m_old_cur_regionConfigArr; std::vector<RegionConfigST> m_old_cur_regionConfigArr;
// 模板信息
cv::RotatedRect m_tplOuterRect; // 模板外接矩形
cv::RotatedRect m_outer_rroi;
cv::RotatedRect m_inner_rroi;
cv::RotatedRect m_tplInnerRect; // 模板内边缘矩形
std::vector<std::vector<cv::Point>> m_tplPinContours; // 模板 pin blob 轮廓点集(pinImg 裁剪坐标系)
std::vector<std::vector<cv::Point>> m_tplPinContoursCur; // 模板 pin 轮廓(反变换到当前检测图坐标系)
std::vector<std::vector<cv::Point>> m_curPinContours; // 当前 pin blob 轮廓点集
cv::Mat m_affine_cur2tpl; // 当前图 -> 模板图
cv::Mat m_affine_tpl2cur; // 模板图 -> 当前图
bool m_bAffineValid = false; // 仿射矩阵是否有效
std::vector<std::pair<cv::Point2f, cv::Point2f>> m_pinFitLines; // 新方案: 两排Pin拟合直线段(当前检测图坐标系, 两端点)
std::vector<cv::Point2f> m_pinMissMarks; // 新方案: 缺失Pin位置(相邻Pin中心连线中点)
}; };
#endif #endif

@ -47,19 +47,23 @@ struct VERSION_INFO
// 检测错误代码 // 检测错误代码
enum ERROR_TYPE_ enum ERROR_TYPE_
{ {
ERROR_TYPE_OK, // 1 疑是 ERROR_TYPE_OK, // 0 好品
ERROR_TYPE_aotudian, // 2 凹凸点 ERROR_TYPE_PIN_PoSun, // 1 PIN破损
ERROR_TYPE_other, // 3 其他 ERROR_TYPE_PIN_QueShi, // 2 PIN缺失
ERROR_TYPE_line, // 4 线状 ERROR_TYPE_BTB_DuanLie, // 3 BTB断裂
ERROR_TYPE_zangwu, // 5 脏污 ERROR_TYPE_LianXi, // 4 连锡
ERROR_TYPE_edge, // 6 边缘 ERROR_TYPE_QueXi, // 5 缺锡
ERROR_TYPE_ymhs, // 7 研磨划伤 ERROR_TYPE_YiWu, // 6 异物
ERROR_TYPE_dianzhuang, // 8 点状 ERROR_TYPE_PIN_PianYi, // 7 PIN偏移
ERROR_TYPE_posun, // 9 破损 ERROR_TYPE_C_BianXing, // 8 C型卡口异常
ERROR_TYPE_xianwei, // 10 纤维 ERROR_TYPE_Panel, // 9 Panel
ERROR_TYPE_shuizi, // 11 水渍 ERROR_TYPE_ZJ_PianYi, // 1 支架偏移
ERROR_TYPE_danban, // 12 淡斑 ERROR_TYPE_ZJ_QueShi, // 2 支架缺失
ERROR_TYPE_fuchen, // 13 浮尘 ERROR_TYPE_CGMo_QueShi, // 3 把手缺失
ERROR_TYPE_CGMark, // 4 把手标记
ERROR_TYPE_BQBMark, // 5 补强板标记
ERROR_TYPE_TBBQ, // 6 铜箔标签
ERROR_TYPE_TBMark, // 7 铜箔标记
ERROR_TYPE_COUNT, ERROR_TYPE_COUNT,
}; };
extern std::vector<std::string> QX_Result_Names; extern std::vector<std::string> QX_Result_Names;
@ -274,11 +278,19 @@ struct DetectInfo
} }
}; };
// 残点检测模型名称:用于区分残点由哪个模型检出(存图分文件夹 + 日志追溯)
#define AI_DET_MODEL_NAME_Defect "Defect"
#define AI_DET_MODEL_NAME_QueXi "Defect_QueXi"
#define AI_DET_MODEL_NAME_UnKnown "UnKnown"
// 多个模型都检出同一个残点时的连接符
#define AI_DET_MODEL_NAME_JOIN "+"
struct QXImageResult struct QXImageResult
{ {
int type; // 缺陷类型 int type; // 缺陷类型
std::string qx_Code; // 缺陷code std::string qx_Code; // 缺陷code
std::string strTypeName; // 缺陷名称 std::string strTypeName; // 缺陷名称
std::string qx_level; // 缺陷等级
cv::Mat srcImg; // 缺陷原始图片 cv::Mat srcImg; // 缺陷原始图片
cv::Mat resizeImg; // 缺陷缩略图 cv::Mat resizeImg; // 缺陷缩略图
@ -343,6 +355,7 @@ struct QXImageResult
max_v = 0; max_v = 0;
strTypeName = ""; strTypeName = "";
qx_Code = ""; qx_Code = "";
qx_level = "";
len = 0; len = 0;
breadth = 0; breadth = 0;
idx = 0; idx = 0;

@ -3,7 +3,7 @@
* @Date: 2025-08-04 21:26:32 * @Date: 2025-08-04 21:26:32
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-08-13 16:05:08 * @LastEditTime: 2025-08-13 16:05:08
* @FilePath: /BOE_CELL_AOI/AlgorithmModule/include/Pin_QX_Det.h * @FilePath: /BOE_ZB_SOCKET/AlgorithmModule/include/Pin_QX_Det.h
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
/* /*
@ -11,7 +11,7 @@
* @Date: 2025-08-04 21:26:32 * @Date: 2025-08-04 21:26:32
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-08-04 21:29:05 * @LastEditTime: 2025-08-04 21:29:05
* @FilePath: /BOE_CELL_AOI/AlgorithmModule/include/Pin_QX_Det.h * @FilePath: /BOE_ZB_SOCKET/AlgorithmModule/include/Pin_QX_Det.h
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
/* /*
@ -59,10 +59,7 @@ public:
BaseCheckFunction *pBaseCheckFunction; BaseCheckFunction *pBaseCheckFunction;
std::string strChannel; std::string strChannel;
std::vector<QX_Result> qx_result; std::string strProductID; // 产品ID(存图文件名用)
std::vector<Det_ROI_Config> pin_det_roi;
std::vector<cv::Point> Det_region;
bool bSaveResultImg; bool bSaveResultImg;
DetConfigResult() DetConfigResult()
{ {
@ -72,106 +69,8 @@ public:
{ {
pBaseCheckFunction = NULL; pBaseCheckFunction = NULL;
strChannel = ""; strChannel = "";
qx_result.clear(); strProductID = "";
pin_det_roi.clear();
Det_region.clear();
bSaveResultImg = false;
}
};
enum Det_ROI_Type
{
Det_ROI_Type_UP,
Det_ROI_Type_DOWN,
Det_ROI_Type_LEFT,
Det_ROI_Type_RIGHT,
};
// 边缘搜索定位结果
struct Pin_AI_Result
{
int nresult;
cv::RotatedRect bigroi;
cv::RotatedRect smallroi;
bool buseOfft;
int offt_x;
int offt_y;
cv::Mat H;
Pin_AI_Result()
{
Init();
}
void Init()
{
buseOfft = false;
nresult = 0;
offt_x = 0;
offt_y = 0;
bigroi = cv::RotatedRect(cv::Point2f(0, 0), cv::Size2f(0, 0), 0);
smallroi = cv::RotatedRect(cv::Point2f(0, 0), cv::Size2f(0, 0), 0);
if (!H.empty())
{
H.release();
/* code */
}
}
};
enum SaveProcessType
{
Save_Close, // 不保存
Save_Filter, // 过滤的
Save_ALL, // 全部
};
struct DetConfig
{
int ncamId; // 相机ID
BaseCheckFunction *pBaseCheckFunction;
std::string strChannel; // 通道
int nthresholdvalue; // 背景阈值
int nAIErodesize; // 边缘腐蚀强度
bool bSaveResultImg; // 保存结果图片
SaveProcessType saveProcessImg; // 保存过程图片
bool bUseDrawRoi_Check; // 是否用绘制的ROI进行校验
cv::Rect drawRoi; // 绘制的 ROi;
cv::Mat drawMask; // 绘制的maksk
DetConfig()
{
Init();
}
void Init()
{
pBaseCheckFunction = NULL;
ncamId = 0;
nthresholdvalue = 1;
nAIErodesize = 7;
bSaveResultImg = false; bSaveResultImg = false;
saveProcessImg = Save_Close;
bUseDrawRoi_Check = false;
drawRoi = cv::Rect(0, 0, 0, 0);
if (!drawMask.empty())
{
drawMask.release();
/* code */
}
}
void Print()
{
printf("nthresholdvalue:%d;nAIErodesize %d;bSaveResultImg %s SaveProcessImg %d\n",
nthresholdvalue, nAIErodesize, BOOL_TO_STR(bSaveResultImg), saveProcessImg);
printf("bUseDrawRoi_Check %s roi %s \n",
BOOL_TO_STR(bUseDrawRoi_Check), CheckUtil::GetRectString(drawRoi).c_str());
}
bool IsSaveProcessImg()
{
if (saveProcessImg != Save_Close)
{
return true;
}
return false;
} }
}; };
@ -179,7 +78,7 @@ public:
Pin_QX_Det(std::shared_ptr<DetLog>& log_ref); Pin_QX_Det(std::shared_ptr<DetLog>& log_ref);
~Pin_QX_Det(); ~Pin_QX_Det();
int Detect(const cv::Mat &img, DetConfigResult *pDetConfig); int GetPinMask(const cv::Mat &img, DetConfigResult *pDetConfig, Mat &mask);
private: private:
int InitModel_Pin(); int InitModel_Pin();
@ -187,17 +86,12 @@ private:
private: private:
bool m_bInitSucc; // 是否初始化成功 bool m_bInitSucc; // 是否初始化成功
// 检测结果
std::shared_ptr<Pin_AI_Result> m_pCheckResult_Pin;
std::shared_ptr<DetLog>& m_pdetlog; std::shared_ptr<DetLog>& m_pdetlog;
bool m_bInitialized;
bool m_bModelSucc;
bool m_bModel_Mark_Succ;
bool m_bshowimg; bool m_bshowimg;
cv::Mat showimg; cv::Mat showimg;
std::string m_strRootPath_Pin; std::string m_strRootPath_Pin;
std::string m_strSavePath_Pin; std::string m_strSavePath_Pin;
std::string m_strLastDate; std::string m_strLastDate;

@ -3,7 +3,7 @@
* @Date: 2025-09-11 15:32:52 * @Date: 2025-09-11 15:32:52
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-09-22 16:56:41 * @LastEditTime: 2025-09-22 16:56:41
* @FilePath: /BOE_CELL_AOI_Detect/AlgorithmModule/src/AI_Edge_Algin.cpp * @FilePath: /BOE_ZB_SOCKET_Detect/AlgorithmModule/src/AI_Edge_Algin.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
@ -183,8 +183,8 @@ AI_Edge_Algin::AI_Edge_Algin(std::shared_ptr<DetLog> &log_ref)
m_bModelSucc = false; m_bModelSucc = false;
m_bModel_Mark_Succ = false; m_bModel_Mark_Succ = false;
m_bshowimg = false; m_bshowimg = false;
m_strRootPath_Big = "/home/aidlux/BOE/Algin/"; m_strRootPath_Big = "/home/aidlux/BOE/Algin/KK/";
m_strRootPath_Mark = "/home/aidlux/BOE/MarkLine/"; m_strRootPath_Mark = "/home/aidlux/BOE/MarkLine/KK/";
creatsavedir(); creatsavedir();
std::string m_strSavePath; std::string m_strSavePath;
@ -246,7 +246,9 @@ int AI_Edge_Algin::Detect(const cv::Mat &img, DetConfig *pDetConfig, std::shared
} }
return re; return re;
} }
re = Get_Edge(1, img, pDetConfig, m_pDetConfig->strChannel, Small_roi); cv::Rect Big_out_roi = Big_roi.boundingRect() & cv::Rect(0, 0, img.cols, img.rows);
cv::Mat Big_outimg_src = img(Big_out_roi).clone();
re = Get_Edge(1, Big_outimg_src, pDetConfig, m_pDetConfig->strChannel, Small_roi);
if (re != 0) if (re != 0)
{ {
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "AICheck_Edge_Small----error %d ", re); m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "AICheck_Edge_Small----error %d ", re);
@ -257,6 +259,15 @@ int AI_Edge_Algin::Detect(const cv::Mat &img, DetConfig *pDetConfig, std::shared
return re; return re;
} }
// Get_Edge 返回的是其输入图坐标系下的 roi;内定位在 Big_roi 裁剪图上执行,
// 这里把裁剪偏移加回,保证 smallroi 与 bigroi 同为原图坐标系
// (下游内外中心偏移对比、模板对齐仿射变换和结果图绘制都依赖该坐标系)
{
cv::Rect bigRect = Big_roi.boundingRect();
Small_roi.center.x += static_cast<float>(bigRect.x);
Small_roi.center.y += static_cast<float>(bigRect.y);
}
m_pCheckResult_Aling->bigroi = Big_roi; m_pCheckResult_Aling->bigroi = Big_roi;
m_pCheckResult_Aling->smallroi = Small_roi; m_pCheckResult_Aling->smallroi = Small_roi;
@ -355,9 +366,14 @@ int AI_Edge_Algin::Get_Edge(int AIModel_type, const cv::Mat &img, DetConfig *pDe
cv::resize(img, detImg, sz); cv::resize(img, detImg, sz);
int re = 0; int re = 0;
cv::Mat mask; cv::Mat mask;
if (detImg.channels() != 1) // 模型输入要求单通道;原图为彩色时按 BGR 顺序转灰度(与 imread(path,0) 的亮度一致)
if (detImg.channels() == 3)
{
cv::cvtColor(detImg, detImg, cv::COLOR_BGR2GRAY);
}
else if (detImg.channels() == 4)
{ {
cv::cvtColor(detImg, detImg, cv::COLOR_RGB2GRAY); cv::cvtColor(detImg, detImg, cv::COLOR_BGRA2GRAY);
} }
re = pSocket_Align->AIDet(detImg, mask); re = pSocket_Align->AIDet(detImg, mask);
@ -370,21 +386,12 @@ int AI_Edge_Algin::Get_Edge(int AIModel_type, const cv::Mat &img, DetConfig *pDe
if (pDetConfig->pBaseCheckFunction->saveImg.bSaveAlginImg) if (pDetConfig->pBaseCheckFunction->saveImg.bSaveAlginImg)
{ {
creatsavedir(); // 前缀用日期(年月日): 同一产品当天的所有图前缀一致; 文件已存在时在日期后追加序号, 不覆盖旧图
static int sdk = 0; std::string strDatePre = CheckUtil::getCurrentDateYMD();
std::string str = m_strSavePath_Big + std::to_string(sdk) + "_" + pDetConfig->strChannel + "_Img.png"; std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + pDetConfig->strProductID + "/KK/";
int sr = m_pImageStorage->addImage(str, detImg); std::string strName = pDetConfig->strChannel + "_" + std::to_string(AIModel_type) + "_Align_out";
if (sr == 0) std::string strSavePath_out = CheckUtil::GetUniqueImgPath(strSaveDir, strDatePre, strName, ".png");
{ CheckUtil::SaveImgAsync(strSavePath_out, mask);
str = m_strSavePath_Big + std::to_string(sdk) + "_" + pDetConfig->strChannel + "_Img_mask.png";
int sr = m_pImageStorage->addImage(str, mask, true);
sdk++;
if (sdk > 99999)
{
sdk = 0;
/* code */
}
}
} }
if (m_pDetConfig->bSaveResultImg) if (m_pDetConfig->bSaveResultImg)

@ -12,99 +12,6 @@
#include "AI_Factory.h" #include "AI_Factory.h"
#include <fstream> #include <fstream>
std::vector<ReadFlawCode> m_FlawCodeList;
std::vector<std::string> QX_Result_Names =
{
"OK",
"aotudian",
"other",
"line",
"zangwu",
"edge",
"ymhs",
"dianzhuang",
"posun",
"xianwei",
"shuizi",
"danban",
"fuchen",
};
std::vector<std::string> QX_Result_Code =
{
"P0000",
"MA507",
"MA508",
"MA506",
"MA504",
"MA503",
"MA502",
"MA505",
"MA501",
"P0001",
"P0002",
"P0003",
"P0004",
};
int ReadFlawCodeConfig(std::string json_path)
{
m_FlawCodeList.erase(m_FlawCodeList.begin(), m_FlawCodeList.end());
std::string strPath = json_path;
printf("ReadFlawCodeConfig path %s\n", strPath.c_str());
Json::CharReaderBuilder builder;
builder["collectComments"] = true;
Json::Value root;
std::string err;
std::ifstream ifs(strPath);
if (!ifs.is_open())
{
printf("error:file is open\n");
return 0;
}
if (!Json::parseFromStream(builder, ifs, &root, &err))
{
printf("error:parseFromStream\n");
return 0;
}
for (int i = 0; i < root.size(); i++)
{
// printf("Node idx %d /%d \n", i, root.size());
ReadFlawCode tem;
tem.flaw_name = root[i]["zh_name"].asString();
tem.flaw_code = root[i]["en_name"].asString();
string desc = root[i]["desc"].asString();
{
std::istringstream stream(desc);
std::string token;
// 使用 getline 按照分号分割
while (std::getline(stream, token, ';'))
{
tem.config_flaw_name.push_back(token);
}
}
m_FlawCodeList.push_back(tem);
}
for(int i = 0; i < m_FlawCodeList.size(); i++)
{
if(i >= QX_Result_Names.size()) {
QX_Result_Names.push_back(m_FlawCodeList.at(i).flaw_name);
QX_Result_Code.push_back(m_FlawCodeList.at(i).flaw_code);
}
else
{
QX_Result_Names.at(i) = m_FlawCodeList.at(i).flaw_name;
QX_Result_Code.at(i) = m_FlawCodeList.at(i).flaw_code;
}
}
return 0;
}
double calculateDistanceBetweenRectCenters(const cv::Rect &rect1, const cv::Rect &rect2, float fx, float fy) double calculateDistanceBetweenRectCenters(const cv::Rect &rect1, const cv::Rect &rect2, float fx, float fy)
{ {
// 计算矩形1的中心点 // 计算矩形1的中心点
@ -128,6 +35,9 @@ ALLImgCheckAnalysisy::ALLImgCheckAnalysisy()
{ {
m_strTest = ""; m_strTest = "";
m_pdetlog = std::make_shared<DetLog>();
m_bInitSucc = false;
m_bExit = false;
} }
ALLImgCheckAnalysisy::~ALLImgCheckAnalysisy() ALLImgCheckAnalysisy::~ALLImgCheckAnalysisy()
@ -180,6 +90,20 @@ int ALLImgCheckAnalysisy::RunStart(void *pconfig1)
m_nErrorCode = re; m_nErrorCode = re;
return m_nErrorCode; return m_nErrorCode;
} }
re = InitTemplateMask();
if (CHECK_OK != re)
{
m_nErrorCode = re;
return m_nErrorCode;
}
// 把模板信息下发到各个相机检测实例
for (auto &item : m_pCameraCheckAnalysisyList)
{
if (item.second)
{
item.second->SetTplInfo(m_tplOuterRect, m_tplInnerRect, m_tplPinContours);
}
}
m_nErrorCode = CHECK_OK; m_nErrorCode = CHECK_OK;
printf(">>>> ALLImgCheckAnalysisy Start Succ \n"); printf(">>>> ALLImgCheckAnalysisy Start Succ \n");
@ -238,6 +162,21 @@ int ALLImgCheckAnalysisy::UpdateConfig(void *pconfig, int nConfigType)
break; break;
case CHECK_CONFIG_Module: case CHECK_CONFIG_Module:
m_pConfigManager = (ConfigManagerBase *)pconfig; m_pConfigManager = (ConfigManagerBase *)pconfig;
// 参数更新时,若系统已初始化,则重新计算模板并下发给各相机
if (m_bInitSucc)
{
re = InitTemplateMask();
if (re == 0)
{
for (auto &item : m_pCameraCheckAnalysisyList)
{
if (item.second)
{
item.second->SetTplInfo(m_tplOuterRect, m_tplInnerRect, m_tplPinContours);
}
}
}
}
break; break;
default: default:
break; break;
@ -307,6 +246,212 @@ int ALLImgCheckAnalysisy::InitAIFactory()
return 0; return 0;
} }
int ALLImgCheckAnalysisy::InitTemplateMask()
{
std::shared_ptr<AIFactory> AI_Factory = AIFactory::GetInstance();
if (!AI_Factory || !AI_Factory->Align_Outer || !AI_Factory->Align_Inner || !AI_Factory->Pin_Loc)
{
return -1;
}
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "---Init Tpl---" , "start");
// 模板图路径,暂时使用固定路径
string tplImgPath = "/home/aidlux/BOE/ZB_SOCKET/Tpl/tpl.bmp"; // 模板图片路径
Mat tplImg = imread(tplImgPath, IMREAD_UNCHANGED);
if (tplImg.empty())
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "", "Failed to load template image!");
return -2;
}
// 模板外接矩形,通过模型Align_Outer得到
RotatedRect tplOuterRect; // 模板外接矩形
std::shared_ptr<AIModel_Base> pSocket_Align;
pSocket_Align = AI_Factory->Align_Outer;
cv::Size sz;
sz.width = pSocket_Align->input_0.width;
sz.height = pSocket_Align->input_0.height;
cv::Mat detImg;
cout << "imgSize: " << tplImg.size() << ", " << "detImgSize: " << detImg.size() << ", " << "szSize: " << sz << endl;
cv::resize(tplImg, detImg, sz);
int re = 0;
cv::Mat mask;
if (detImg.channels() != 1)
{
cv::cvtColor(detImg, detImg, cv::COLOR_RGB2GRAY);
}
re = pSocket_Align->AIDet(detImg, mask);
if (re != 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "AICheck_Edge----error %d ", re);
int re123 = 100 + re;
return re123;
}
// 找到最大轮廓
bool found;
std::vector<cv::Point> boundingBox = CheckUtil::getLargestContourROI(mask, found);
if (!found)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "AI_Edge_Algin ", "No contours found!----error ");
return 4;
}
// 边缘轮廓点集还原为原图坐标,并计算得到RotateROI
if (sz.width <= 0 || sz.height <= 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "AI_Edge_Algin ", "invalid model input size!----error ");
return 5;
}
// 检测小图 到 原图 的缩放比例
const float scale_x = static_cast<float>(tplImg.cols) / sz.width;
const float scale_y = static_cast<float>(tplImg.rows) / sz.height;
// 轮廓点还原到原图坐标系
std::vector<cv::Point2f> srcContour;
srcContour.reserve(boundingBox.size());
for (const auto &pt : boundingBox)
{
srcContour.emplace_back(pt.x * scale_x, pt.y * scale_y);
}
if (srcContour.size() < 3)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "AI_Edge_Algin ", "contour points too few!----error ");
return 6;
}
tplOuterRect = cv::minAreaRect(srcContour);
// 模板内边缘矩形,通过模型Align_Inner得到
// 与 AI_Edge_Algin::Detect 的内定位流程保持一致:按外接矩形裁剪模板图 -> Align_Inner 推理 -> 轮廓还原并把裁剪偏移加回,得到模板图坐标系下的内边缘矩形
// PIN 偏移/缺失检测的仿射变换以内定位为基准,模板侧需先准备好该矩形;计算失败时保持空值,运行时自动回退外定位
cv::RotatedRect tplInnerRect;
bool bTplInnerOk = false;
std::shared_ptr<AIModel_Base> pSocket_AlignInner = AI_Factory->Align_Inner;
cv::Rect tplOuterBound = tplOuterRect.boundingRect() & cv::Rect(0, 0, tplImg.cols, tplImg.rows);
if (pSocket_AlignInner && tplOuterBound.width > 0 && tplOuterBound.height > 0)
{
cv::Mat innerCropImg = tplImg(tplOuterBound).clone();
cv::Size innerSz;
innerSz.width = pSocket_AlignInner->input_0.width;
innerSz.height = pSocket_AlignInner->input_0.height;
cv::Mat innerDetImg;
cv::resize(innerCropImg, innerDetImg, innerSz);
if (innerDetImg.channels() != 1)
{
// 与运行时 AI_Edge_Algin::Get_Edge 内定位的灰度化保持一致(BGR 顺序)
cv::cvtColor(innerDetImg, innerDetImg, cv::COLOR_BGR2GRAY);
}
cv::Mat innerMask;
int reInner = pSocket_AlignInner->AIDet(innerDetImg, innerMask);
if (reInner != 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "AICheck_Inner----error %d ", reInner);
}
else
{
// 找到最大轮廓
bool innerFound = false;
std::vector<cv::Point> innerBoundingBox = CheckUtil::getLargestContourROI(innerMask, innerFound);
if (!innerFound || innerBoundingBox.size() < 3)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "Inner align: No contours found!----error ");
}
else
{
// 内定位小图 到 裁剪图 的缩放比例
const float inner_scale_x = static_cast<float>(innerCropImg.cols) / innerSz.width;
const float inner_scale_y = static_cast<float>(innerCropImg.rows) / innerSz.height;
std::vector<cv::Point2f> innerContour;
innerContour.reserve(innerBoundingBox.size());
for (const auto &pt : innerBoundingBox)
{
innerContour.emplace_back(pt.x * inner_scale_x, pt.y * inner_scale_y);
}
tplInnerRect = cv::minAreaRect(innerContour);
// 裁剪图坐标 -> 模板图坐标(与 AI_Edge_Algin::Detect 中 smallroi 偏移加回一致)
tplInnerRect.center.x += static_cast<float>(tplOuterBound.x);
tplInnerRect.center.y += static_cast<float>(tplOuterBound.y);
bTplInnerOk = true;
}
}
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "Inner align: model null or outer bound invalid!----error ");
}
if (bTplInnerOk)
{
m_tplInnerRect = tplInnerRect;
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl",
"tplInnerRect center(%.1f %.1f) size(%.1f %.1f) angle %.1f",
tplInnerRect.center.x, tplInnerRect.center.y,
tplInnerRect.size.width, tplInnerRect.size.height, tplInnerRect.angle);
}
else
{
m_tplInnerRect = cv::RotatedRect();
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "Init Tpl", "Tpl Inner align invalid, runtime fallback to Outer align!");
}
// 模板Pinmask,通过模型Pin_Loc得到
Mat tplPinMask; // 模板mask
std::shared_ptr<AIModel_Base> pSocket_Pin;
pSocket_Pin = AI_Factory->Pin_Loc;
sz.width = pSocket_Pin->input_0.width;
sz.height = pSocket_Pin->input_0.height;
cv::Mat pinImg = tplImg(tplOuterRect.boundingRect()).clone(); // 裁剪模板图像为外接矩形区域
cout << "imgSize: " << tplImg.size() << ", " << "pinImgSize: " << pinImg.size() << ", " << "szSize: " << sz << endl;
cv::resize(pinImg, detImg, sz);
if (detImg.channels() != 1)
{
cv::cvtColor(detImg, detImg, cv::COLOR_RGB2GRAY);
}
re = pSocket_Pin->AIDet(detImg, tplPinMask);
if (re != 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "AI_Pin ", "AICheck_Pin----error %d ", re);
int re123 = 100 + re;
return re123;
}
// 统计tplPinMask的blob边缘点,并绘制到tplImg上
vector<vector<Point>> pin_contours;
vector<Vec4i> hierarchy;
cv::findContours(tplPinMask, pin_contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
// mask 尺寸 -> 裁剪图(pinImg) 尺寸 的缩放比例
const float pin_scale_x = (tplPinMask.cols > 0) ? static_cast<float>(pinImg.cols) / tplPinMask.cols : 0.0f;
const float pin_scale_y = (tplPinMask.rows > 0) ? static_cast<float>(pinImg.rows) / tplPinMask.rows : 0.0f;
// 将轮廓点从 mask 坐标系 映射回 tplImg 坐标系
m_tplPinContours.clear();
m_tplPinContours.reserve(pin_contours.size());
for (size_t i = 0; i < pin_contours.size(); i++)
{
std::vector<cv::Point> mapped;
mapped.reserve(pin_contours[i].size());
for (const auto &pt : pin_contours[i])
{
mapped.emplace_back(cvRound(pt.x * pin_scale_x),
cvRound(pt.y * pin_scale_y));
}
m_tplPinContours.push_back(mapped);
}
// 保存模板外接矩形
m_tplOuterRect = tplOuterRect;
// 绘制到 tplImg 上
{
Mat showImg = pinImg.clone();
cv::drawContours(pinImg, m_tplPinContours, -1, Scalar(0, 255, 0), 2);
cv::imwrite("/home/aidlux/BOE/ZB_SOCKET/Tpl/tpl_pin_contours.png", pinImg);
}
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_1, "---Init Tpl---" , "end");
return 0;
}
int ALLImgCheckAnalysisy::LoadRunConfig(void *p) int ALLImgCheckAnalysisy::LoadRunConfig(void *p)
{ {
return 0; return 0;
@ -370,14 +515,6 @@ int ALLImgCheckAnalysisy::ExitSystem()
int ALLImgCheckAnalysisy::InitCameraCheckAnalysisy() int ALLImgCheckAnalysisy::InitCameraCheckAnalysisy()
{ {
string defect_list_file = m_pConfigManager->GetJsonPath();
if (defect_list_file == "") {
defect_list_file = "/var/aidlux/efs/model/defect_list.json";
}
else{
defect_list_file += "/defect_list.json";
}
ReadFlawCodeConfig(defect_list_file);
m_pCameraCheckAnalysisyList.clear(); m_pCameraCheckAnalysisyList.clear();
for (const auto &config : m_pConfigManager->Config_instances_) for (const auto &config : m_pConfigManager->Config_instances_)
{ {
@ -418,7 +555,8 @@ int ALLImgCheckAnalysisy::InitData()
int ALLImgCheckAnalysisy::Det_Product(std::shared_ptr<Product> &product) int ALLImgCheckAnalysisy::Det_Product(std::shared_ptr<Product> &product)
{ {
string cur_time_s = CheckUtil::getCurTimeHMS();
printf("[%s]>>>>>>>>>>>>>>>Det_Product****************det Start************\n", cur_time_s.c_str());
// 处理每个相机 // 处理每个相机
while (true) while (true)
{ {
@ -447,8 +585,8 @@ int ALLImgCheckAnalysisy::Det_Product(std::shared_ptr<Product> &product)
// AnalysiyAll(0); // AnalysiyAll(0);
SetProductResult(product); SetProductResult(product);
product->SetCheckEnd(); product->SetCheckEnd();
string cur_time = CheckUtil::getCurTimeHMS(); string cur_time_e = CheckUtil::getCurTimeHMS();
printf("[%s]>>>>>>>>>>>>>>>Det_Product****************det End************\n", cur_time.c_str()); printf("[%s]>>>>>>>>>>>>>>>Det_Product****************det End************\n", cur_time_e.c_str());
return 0; return 0;
} }
@ -485,7 +623,8 @@ int ALLImgCheckAnalysisy::SetProductResult(std::shared_ptr<Product> product)
std::shared_ptr<CheckResult> pCheckResult = pcam->ImageALLDetResultList.at(i)->result; std::shared_ptr<CheckResult> pCheckResult = pcam->ImageALLDetResultList.at(i)->result;
if (pCheckResult->qxImageResult.size() > 0) // 图像 NG(包括图像模糊在 AI 检测之前直接判定的 NG) 也要计入产品级结果
if (pCheckResult->qxImageResult.size() > 0 || pCheckResult->nresult > ERROR_TYPE_OK)
{ {
bNG = true; bNG = true;
} }
@ -550,6 +689,28 @@ int ALLImgCheckAnalysisy::PushInImg_New(std::shared_ptr<shareImage> p)
return CHECK_ERROR_Config_Null; return CHECK_ERROR_Config_Null;
} }
// 对传入图片做一步判断,如果高大于宽,则顺时针旋转90°
if (p->Det_Mode != DET_MODE_ReJson)
{
if (!p->img.empty())
{
cv::Mat imgRot;
cv::rotate(p->img, imgRot, cv::ROTATE_90_CLOCKWISE);
printf("PushInImg rotate 90CW: SN %s channel %s img [%d x %d] -> [%d x %d]\n",
p->strImgProductID.c_str(), p->strChannel.c_str(),
p->img.cols, p->img.rows, imgRot.cols, imgRot.rows);
p->img = imgRot;
}
if (!p->img_B.empty())
{
cv::Mat imgBRot;
cv::rotate(p->img_B, imgBRot, cv::ROTATE_90_CLOCKWISE);
printf("PushInImg rotate 90CW: SN %s channel %s img_B [%d x %d] -> [%d x %d]\n",
p->strImgProductID.c_str(), p->strChannel.c_str(),
p->img_B.cols, p->img_B.rows, imgBRot.cols, imgBRot.rows);
p->img_B = imgBRot;
}
}
std::string strlog = ""; std::string strlog = "";
m_strTest += " PushInImg->SN:"; m_strTest += " PushInImg->SN:";
m_strTest += p->strImgProductID; m_strTest += p->strImgProductID;

@ -7,8 +7,10 @@
* @FilePath: /ZCXD_MonitorPlatform/src/CoreLogicModule/src/CamDeal.cpp * @FilePath: /ZCXD_MonitorPlatform/src/CoreLogicModule/src/CamDeal.cpp
*/ */
#include "CameraCheckAnalysisy.hpp" #include "CameraCheckAnalysisy.hpp"
#include "ImgCheckAnalysisy.hpp"
#include "CheckUtil.hpp" #include "CheckUtil.hpp"
#include "Define.h" #include "Define.h"
#include <atomic>
CameraCheckAnalysisy::CameraCheckAnalysisy() CameraCheckAnalysisy::CameraCheckAnalysisy()
{ {
@ -66,9 +68,19 @@ int CameraCheckAnalysisy::WaitDetImg()
// 判断物料在左还是在右 // 判断物料在左还是在右
string JudgeMaterialPosition(Mat img) { string JudgeMaterialPosition(Mat img) {
if (img.empty() || img.channels() != 1) { if (img.empty()) {
cerr << "Error: Input image is empty or not grayscale!" << endl; cerr << "Error: Input image is empty!" << endl;
return "Unknown"; return "Unknown";
}
// 原图可能是彩色,内部统一转灰度计算(形参为副本,不会改动调用方图像)
if (img.channels() != 1) {
cv::Mat grayImg;
if (img.channels() == 4) {
cv::cvtColor(img, grayImg, cv::COLOR_BGRA2GRAY);
} else {
cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY);
}
img = grayImg;
} }
resize(img, img, Size(img.cols, img.rows * 0.001), 0, 0); resize(img, img, Size(img.cols, img.rows * 0.001), 0, 0);
int rows = img.rows; int rows = img.rows;
@ -351,8 +363,23 @@ int CameraCheckAnalysisy::GetRectAndPoint(const cv::Mat &img, cv::Rect &roi, cv:
// printf("DirectSign_UP::111111111111111111 re123 %d\n", re123); // printf("DirectSign_UP::111111111111111111 re123 %d\n", re123);
// getchar(); // getchar();
// 阈值化 + 轮廓提取只支持单通道,彩色原图内部转灰度(不改动调用方图像)
cv::Mat grayImg;
if (img.channels() != 1)
{
if (img.channels() == 4)
{
cv::cvtColor(img, grayImg, cv::COLOR_BGRA2GRAY);
}
else
{
cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY);
}
}
const cv::Mat &workImg = (img.channels() == 1) ? img : grayImg;
cv::Mat small, smallbin; cv::Mat small, smallbin;
cv::resize(img, small, cv::Size(600, 1500)); cv::resize(workImg, small, cv::Size(600, 1500));
cv::threshold(small, smallbin, 15, 255, cv::threshold(small, smallbin, 15, 255,
cv::THRESH_BINARY); cv::THRESH_BINARY);
@ -385,8 +412,8 @@ int CameraCheckAnalysisy::GetRectAndPoint(const cv::Mat &img, cv::Rect &roi, cv:
cv::Rect rSmall = cv::boundingRect(contours[idx]); cv::Rect rSmall = cv::boundingRect(contours[idx]);
/* 5. map to original */ /* 5. map to original */
float sx = (float)img.cols / smallbin.cols; float sx = (float)workImg.cols / smallbin.cols;
float sy = (float)img.rows / smallbin.rows; float sy = (float)workImg.rows / smallbin.rows;
cv::Rect rectInSrc; cv::Rect rectInSrc;
rectInSrc.x = int(rSmall.x * sx) - 50; rectInSrc.x = int(rSmall.x * sx) - 50;
@ -407,17 +434,17 @@ int CameraCheckAnalysisy::GetRectAndPoint(const cv::Mat &img, cv::Rect &roi, cv:
rectInSrc.x = 0; rectInSrc.x = 0;
if (rectInSrc.y < 0) if (rectInSrc.y < 0)
rectInSrc.y = 0; rectInSrc.y = 0;
if (rectInSrc.x + rectInSrc.width > img.cols) if (rectInSrc.x + rectInSrc.width > workImg.cols)
rectInSrc.width = img.cols - rectInSrc.x; rectInSrc.width = workImg.cols - rectInSrc.x;
if (rectInSrc.y + rectInSrc.height > img.rows) if (rectInSrc.y + rectInSrc.height > workImg.rows)
{ {
rectInSrc.height = img.rows - rectInSrc.y; rectInSrc.height = workImg.rows - rectInSrc.y;
} }
merg_p_up.y = A_line_up; merg_p_up.y = A_line_up;
merg_p_up.x = img.cols; merg_p_up.x = workImg.cols;
merg_p_down.y = A_line_down; merg_p_down.y = A_line_down;
merg_p_down.x = img.cols; merg_p_down.x = workImg.cols;
roi = rectInSrc; roi = rectInSrc;
if (bshowimg) if (bshowimg)
@ -601,6 +628,20 @@ int CameraCheckAnalysisy::Init(std::string strcameraName)
return 0; return 0;
} }
void CameraCheckAnalysisy::SetTplInfo(const cv::RotatedRect &outerRect,
const cv::RotatedRect &innerRect,
const std::vector<std::vector<cv::Point>> &pinContours)
{
for (int i = 0; i < IMGCHECKANALYSISY_NUM; i++)
{
auto *p = static_cast<ImgCheckAnalysisy *>(m_pImgCheckAnalysisy[i]);
if (p)
{
p->SetTplInfo(outerRect, innerRect, pinContours);
}
}
}
int CameraCheckAnalysisy::StartCheck(std::shared_ptr<CameraResult> pCamera_Check_Result) int CameraCheckAnalysisy::StartCheck(std::shared_ptr<CameraResult> pCamera_Check_Result)
{ {
if (m_bHaveImgeDet) if (m_bHaveImgeDet)
@ -662,10 +703,18 @@ int CameraCheckAnalysisy::InitRun()
int CameraCheckAnalysisy::InitCheckAnalysisy() int CameraCheckAnalysisy::InitCheckAnalysisy()
{ {
int re = 0; int re = 0;
// 全局递增的 CPU 核偏移:
static std::atomic<int> s_cpuOffset{0};
for (int i = 0; i < IMGCHECKANALYSISY_NUM; i++) for (int i = 0; i < IMGCHECKANALYSISY_NUM; i++)
{ {
RunInfoST RunConfig; RunInfoST RunConfig;
RunConfig.nThreadIdx = i; RunConfig.nThreadIdx = i;
{
// 核带下沉到库内部:不再依赖调用方传入的 nCpu_start_Idx / nCpu_num
int offset = s_cpuOffset.fetch_add(1) % CHECK_CPU_BAND_NUM;
RunConfig.nCpu_start_Idx = CHECK_CPU_BAND_START + offset;
}
RunConfig.nCpu_num = 1;
m_pImgCheckAnalysisy[i] = ImgCheckBase::GetInstance(); m_pImgCheckAnalysisy[i] = ImgCheckBase::GetInstance();
re = m_pImgCheckAnalysisy[i]->UpdateConfig((void *)&RunConfig, CHECK_CONFIG_Run); re = m_pImgCheckAnalysisy[i]->UpdateConfig((void *)&RunConfig, CHECK_CONFIG_Run);
@ -767,7 +816,6 @@ int CameraCheckAnalysisy::CheckImgRun()
if (m_pCheck_Result->getImgPushComplate()) if (m_pCheck_Result->getImgPushComplate())
{ {
curcheckStep = Check_Step_Complete; // 进入到预图片检测阶段 curcheckStep = Check_Step_Complete; // 进入到预图片检测阶段
m_pCheck_Result->SetCheckStep(curcheckStep);
} }
else else
{ {
@ -784,7 +832,12 @@ int CameraCheckAnalysisy::CheckImgRun()
m_pdetlog->AddCheckstr(PrintLevel_0, "CheckImgRun", "========came %s Check ALL end time %ld ms %s", m_pdetlog->AddCheckstr(PrintLevel_0, "CheckImgRun", "========came %s Check ALL end time %ld ms %s",
m_pCheck_Result->cameraBaseResult->strCameraName.c_str(), tend - m_pCheck_Result->time_start, strTimg.c_str()); m_pCheck_Result->cameraBaseResult->strCameraName.c_str(), tend - m_pCheck_Result->time_start, strTimg.c_str());
InsertCameraLog(); InsertCameraLog();
// 结果与日志(det_LogList)全部写完后,才置为 Check_Step_Complete,
// 通知 Det_Product 结果可以交付调用方;并且整个收尾过程保持在
// mtx_WaiteImg 锁内,与 StartCheck() 对 m_pCheck_Result 的赋值互斥,
// 避免同一 shared_ptr 被并发 reset/赋值(会造成控制块双重释放)。
std::lock_guard<std::mutex> lock(mtx_WaiteImg); std::lock_guard<std::mutex> lock(mtx_WaiteImg);
m_pCheck_Result->SetCheckStep(Check_Step_Complete);
m_bHaveImgeDet = false; m_bHaveImgeDet = false;
m_pCheck_Result.reset(); m_pCheck_Result.reset();
@ -828,8 +881,12 @@ int CameraCheckAnalysisy::InsertCameraLog()
} }
// 1、把图片的相关日志 写到 返回 日志中。 // 1、把图片的相关日志 写到 返回 日志中。
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(), // 注意:DetLog 可能仍被实例线程写入,使用 GetLogs() 做加锁快照,避免并发遍历。
pImageResult->detlog->logList.begin(), pImageResult->detlog->logList.end()); {
std::vector<std::string> imgLogs = pImageResult->detlog->GetLogs();
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(),
imgLogs.begin(), imgLogs.end());
}
std::string str = ""; std::string str = "";
char buffer[256]; char buffer[256];
// 2、把缺陷信息 写的 返回日志中。 // 2、把缺陷信息 写的 返回日志中。
@ -860,8 +917,9 @@ int CameraCheckAnalysisy::InsertCameraLog()
if (QX_info->result == QX_RESULT_TYPE_NG) if (QX_info->result == QX_RESULT_TYPE_NG)
{ {
std::shared_ptr<DetLog> pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog; std::shared_ptr<DetLog> pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog;
std::vector<std::string> qxLogs = pQxLog->GetLogs();
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(), m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(),
pQxLog->logList.begin(), pQxLog->logList.end()); qxLogs.begin(), qxLogs.end());
} }
} }
@ -877,8 +935,9 @@ int CameraCheckAnalysisy::InsertCameraLog()
if (QX_info->result == QX_RESULT_TYPE_YS) if (QX_info->result == QX_RESULT_TYPE_YS)
{ {
std::shared_ptr<DetLog> pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog; std::shared_ptr<DetLog> pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog;
std::vector<std::string> qxLogs = pQxLog->GetLogs();
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(), m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(),
pQxLog->logList.begin(), pQxLog->logList.end()); qxLogs.begin(), qxLogs.end());
} }
} }
@ -894,8 +953,9 @@ int CameraCheckAnalysisy::InsertCameraLog()
if (QX_info->result == QX_RESULT_TYPE_OK) if (QX_info->result == QX_RESULT_TYPE_OK)
{ {
std::shared_ptr<DetLog> pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog; std::shared_ptr<DetLog> pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog;
std::vector<std::string> qxLogs = pQxLog->GetLogs();
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(), m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(),
pQxLog->logList.begin(), pQxLog->logList.end()); qxLogs.begin(), qxLogs.end());
} }
} }
@ -911,8 +971,9 @@ int CameraCheckAnalysisy::InsertCameraLog()
if (QX_info->result == QX_RESULT_TYPE_NoJduge) if (QX_info->result == QX_RESULT_TYPE_NoJduge)
{ {
std::shared_ptr<DetLog> pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog; std::shared_ptr<DetLog> pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog;
std::vector<std::string> qxLogs = pQxLog->GetLogs();
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(), m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(),
pQxLog->logList.begin(), pQxLog->logList.end()); qxLogs.begin(), qxLogs.end());
} }
} }
} }
@ -924,8 +985,11 @@ int CameraCheckAnalysisy::InsertCameraLog()
} }
// 3、把相机的日志 写到 返回日志中。 // 3、把相机的日志 写到 返回日志中。
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(), {
m_pCheck_Result->detlog->logList.begin(), m_pCheck_Result->detlog->logList.end()); std::vector<std::string> camLogs = m_pCheck_Result->detlog->GetLogs();
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(),
camLogs.begin(), camLogs.end());
}
{ {
sprintf(buffer, " =====================Product Log ============================="); sprintf(buffer, " =====================Product Log =============================");
@ -933,8 +997,11 @@ int CameraCheckAnalysisy::InsertCameraLog()
m_CheckResult_shareP->det_LogList.push_back(str); m_CheckResult_shareP->det_LogList.push_back(str);
} }
// 4、把产品相关日志 写到 返回日志中。 // 4、把产品相关日志 写到 返回日志中。
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(), {
m_pCheck_Result->productBaseResult->detlog->logList.begin(), m_pCheck_Result->productBaseResult->detlog->logList.end()); std::vector<std::string> productLogs = m_pCheck_Result->productBaseResult->detlog->GetLogs();
m_CheckResult_shareP->det_LogList.insert(m_CheckResult_shareP->det_LogList.end(),
productLogs.begin(), productLogs.end());
}
} }
return 0; return 0;
} }
@ -1052,20 +1119,28 @@ ImgCheckBase *CameraCheckAnalysisy::GetDealResult(int idx)
return NULL; return NULL;
} }
int CameraCheckAnalysisy::GetEdgePoint(const cv::Mat &img, Edge_Search_Config *pEdge_Search_Config, int &outP) int CameraCheckAnalysisy::GetEdgePoint(const cv::Mat &imgSrc, Edge_Search_Config *pEdge_Search_Config, int &outP)
{ {
bool bdbuge = false; bool bdbuge = false;
bdbuge = bshowimg; bdbuge = bshowimg;
if (img.empty()) if (imgSrc.empty())
{ {
return -11; return -11;
} }
if (img.channels() != 1) // 边缘搜索仅支持单通道;彩色/BGRA 原图在此内部转灰度(不改动调用方图像)
cv::Mat grayImg;
if (imgSrc.channels() != 1)
{ {
// printf("*****************************channels\n"); if (imgSrc.channels() == 4)
{
return -12; cv::cvtColor(imgSrc, grayImg, cv::COLOR_BGRA2GRAY);
}
else
{
cv::cvtColor(imgSrc, grayImg, cv::COLOR_BGR2GRAY);
}
} }
const cv::Mat &img = (imgSrc.channels() == 1) ? imgSrc : grayImg;
// 检查roi; // 检查roi;
if (!CheckUtil::RoiInImg(pEdge_Search_Config->roi, img)) if (!CheckUtil::RoiInImg(pEdge_Search_Config->roi, img))
{ {

@ -3,7 +3,7 @@
* @Date: 2025-09-13 20:39:37 * @Date: 2025-09-13 20:39:37
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-09-17 16:39:47 * @LastEditTime: 2025-09-17 16:39:47
* @FilePath: /BOE_CELL_AOI_Detect/AlgorithmModule/src/CameraResult.cpp * @FilePath: /BOE_ZB_SOCKET_Detect/AlgorithmModule/src/CameraResult.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
#include "CameraResult.h" #include "CameraResult.h"

@ -18,6 +18,11 @@ Json::Value CheckResultJson::toJsonValue()
root["Param_CropRoi"]["width"] = m_pOneImgDetResult->Param_CropRoi.width; root["Param_CropRoi"]["width"] = m_pOneImgDetResult->Param_CropRoi.width;
root["Param_CropRoi"]["height"] = m_pOneImgDetResult->Param_CropRoi.height; root["Param_CropRoi"]["height"] = m_pOneImgDetResult->Param_CropRoi.height;
} }
{
// 图像模糊度(清晰度)检测结果: 在 AI 检测之前计算
root["blurScore"] = m_pOneImgDetResult->fBlurScore;
root["isBlur"] = m_pOneImgDetResult->bBlurNG;
}
for (int i = 0; i < m_pOneImgDetResult->pQx_ErrorList->size(); i++) for (int i = 0; i < m_pOneImgDetResult->pQx_ErrorList->size(); i++)
{ {
QX_ERROR_INFO_ *p = &m_pOneImgDetResult->pQx_ErrorList->at(i); QX_ERROR_INFO_ *p = &m_pOneImgDetResult->pQx_ErrorList->at(i);
@ -38,6 +43,8 @@ Json::Value CheckResultJson::toJsonValue()
value["flen"] = p->flen; value["flen"] = p->flen;
value["nconfig_qx_type"] = p->nconfig_qx_type; value["nconfig_qx_type"] = p->nconfig_qx_type;
value["qx_name"] = p->qx_name; value["qx_name"] = p->qx_name;
value["qx_level"] = p->qx_level;
value["det_model_name"] = p->strModelName;
value["maxValue"] = p->maxValue; value["maxValue"] = p->maxValue;
value["grayDis"] = p->grayDis; value["grayDis"] = p->grayDis;
value["fUpIou"] = p->fUpIou; value["fUpIou"] = p->fUpIou;
@ -84,6 +91,17 @@ void CheckResultJson::toObjectFromValue(Json::Value root)
m_pOneImgDetResult->Param_CropRoi.height = root["Param_CropRoi"]["height"].asInt(); m_pOneImgDetResult->Param_CropRoi.height = root["Param_CropRoi"]["height"].asInt();
} }
} }
{
// 图像模糊度(清晰度)检测结果; 旧版本 json 可能没有该字段
if (root.isMember("blurScore"))
{
m_pOneImgDetResult->fBlurScore = root["blurScore"].asFloat();
}
if (root.isMember("isBlur"))
{
m_pOneImgDetResult->bBlurNG = root["isBlur"].asBool();
}
}
{ {
if (root.isMember("Qx")) if (root.isMember("Qx"))
{ {
@ -117,6 +135,16 @@ void CheckResultJson::toObjectFromValue(Json::Value root)
tem.flen = errorJson["flen"].asFloat(); tem.flen = errorJson["flen"].asFloat();
tem.nconfig_qx_type = errorJson["nconfig_qx_type"].asInt(); tem.nconfig_qx_type = errorJson["nconfig_qx_type"].asInt();
tem.qx_name = errorJson["qx_name"].asString(); tem.qx_name = errorJson["qx_name"].asString();
// 旧版本 json 可能没有 qx_level 字段
if (errorJson.isMember("qx_level"))
{
tem.qx_level = errorJson["qx_level"].asString();
}
// 旧版本 json 可能没有该字段
if (errorJson.isMember("det_model_name"))
{
tem.strModelName = errorJson["det_model_name"].asString();
}
tem.maxValue = errorJson["maxValue"].asInt(); tem.maxValue = errorJson["maxValue"].asInt();
tem.grayDis = errorJson["grayDis"].asFloat(); tem.grayDis = errorJson["grayDis"].asFloat();
tem.fUpIou = errorJson["fUpIou"].asFloat(); tem.fUpIou = errorJson["fUpIou"].asFloat();

@ -19,6 +19,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <fstream> #include <fstream>
#include <thread> #include <thread>
#include <fcntl.h>
#include "snowflake.hpp" #include "snowflake.hpp"
int _sysmkdir_2(const std::string &dir) int _sysmkdir_2(const std::string &dir)
@ -67,8 +68,10 @@ std::string CheckUtil::getCurrentDate()
// 将 tv_sec 转换为 time_t 类型,代表自1970年1月1日起的秒数 // 将 tv_sec 转换为 time_t 类型,代表自1970年1月1日起的秒数
time_t raw_time = tv.tv_sec; time_t raw_time = tv.tv_sec;
// 使用 localtime 将 time_t 转换为 tm 结构体 // 使用 localtime_r 转换为 tm 结构体。
struct tm *time_info = localtime(&raw_time); // 注意:localtime() 返回共享的静态缓冲区,多线程并发调用会互相覆盖(tm 内容串数据)。
struct tm time_buf;
struct tm *time_info = localtime_r(&raw_time, &time_buf);
// 使用字符串流格式化为 "YYYY-MM-DD" // 使用字符串流格式化为 "YYYY-MM-DD"
std::ostringstream oss; std::ostringstream oss;
@ -78,20 +81,41 @@ std::string CheckUtil::getCurrentDate()
return oss.str(); // 返回日期字符串 return oss.str(); // 返回日期字符串
} }
std::string CheckUtil::getCurrentDateYMD()
{
struct timeval tv;
gettimeofday(&tv, NULL);
time_t raw_time = tv.tv_sec;
// localtime_r 为线程安全版本
struct tm time_buf;
struct tm *time_info = localtime_r(&raw_time, &time_buf);
// 固定 8 位 "YYYYMMDD", 便于文件名检索与排序
std::ostringstream oss;
oss << std::setfill('0')
<< std::setw(4) << (time_info->tm_year + 1900)
<< std::setw(2) << (time_info->tm_mon + 1)
<< std::setw(2) << time_info->tm_mday;
return oss.str();
}
std::string CheckUtil::getCurTimeHMS() std::string CheckUtil::getCurTimeHMS()
{ {
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); // 获取当前时间 gettimeofday(&tv, NULL); // 获取当前时间
time_t raw_time = tv.tv_sec; time_t raw_time = tv.tv_sec;
struct tm *time_info = localtime(&raw_time); // 使用 localtime_r(线程安全);localtime() 的静态缓冲会被其它线程并发覆盖
struct tm time_buf;
struct tm *time_info = localtime_r(&raw_time, &time_buf);
int millisec = tv.tv_usec / 1000; // 微秒转毫秒 int millisec = tv.tv_usec / 1000; // 微秒转毫秒
std::ostringstream oss; std::ostringstream oss;
oss << std::setfill('0') << std::setw(2) << time_info->tm_hour << ":" oss << std::setfill('0') << std::setw(2) << time_info->tm_hour << "-"
<< std::setfill('0') << std::setw(2) << time_info->tm_min << ":" << std::setfill('0') << std::setw(2) << time_info->tm_min << "-"
<< std::setfill('0') << std::setw(2) << time_info->tm_sec << "." << std::setfill('0') << std::setw(2) << time_info->tm_sec << "-"
<< std::setfill('0') << std::setw(3) << millisec; << std::setfill('0') << std::setw(3) << millisec;
return oss.str(); // 返回格式 "HH:MM:SS.mmm" return oss.str(); // 返回格式 "HH:MM:SS.mmm"
@ -118,6 +142,40 @@ int64_t CheckUtil::getSnowId()
return 0; return 0;
} }
std::string CheckUtil::GetUniqueImgPath(const std::string &strDir, const std::string &strPrefix, const std::string &strName, const std::string &strExt)
{
if (strDir.empty())
{
return strDir + strPrefix + strName + strExt;
}
// 目录不存在时 O_CREAT 会失败, 先建目录(已存在时内部无副作用)
CreateDir(strDir);
// 去重序号插在前缀之后: <年月日>[_序号]_<通道>_<用途>.png
// 同一产品的每个 strName 每轮只会写一次, 所以各文件序号天然同步:
// 第 1 轮 -> <年月日>_<名称>.png、第 2 轮 -> <年月日>_1_<名称>.png、第 3 轮 -> <年月日>_2_<名称>.png ...
const std::string strHead = strPrefix.empty() ? std::string("") : (strPrefix + "_");
for (int i = 0; i < 100000; i++)
{
std::string strPath = strDir + strHead + (i == 0 ? std::string("") : std::to_string(i) + "_") + strName + strExt;
// O_EXCL: 文件不存在才能创建成功, 即是"原子占位", 并发安全
int fd = ::open(strPath.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644);
if (fd >= 0)
{
::close(fd);
return strPath;
}
if (errno != EEXIST)
{
// 非"已存在"类错误(权限/路径等)不再重试, 直接返回让 imwrite 把错误暴露出来
printf("create img file[%s] error: %d %s\n", strPath.c_str(), errno, strerror(errno));
return strPath;
}
}
// 序号用尽的兜底(实际不会发生)
return strDir + strHead + std::to_string(CheckUtil::getcurTime()) + "_" + strName + strExt;
}
bool CheckUtil::JudgRect(cv::Rect roi, int img_w, int img_h) bool CheckUtil::JudgRect(cv::Rect roi, int img_w, int img_h)
{ {
if (img_w <= 0 || img_h <= 0) if (img_w <= 0 || img_h <= 0)
@ -419,6 +477,26 @@ float CheckUtil::getLongAxisAngle(cv::RotatedRect rect) {
return long_axis_angle; return long_axis_angle;
} }
std::vector<cv::Point2f> CheckUtil::sort_vertices(cv::RotatedRect rrect) {
cv::Point2f pts[4];
rrect.points(pts);
std::vector<cv::Point2f> vertices(pts, pts + 4);
// 按 y 坐标升序排序
std::sort(vertices.begin(), vertices.end(),
[](const cv::Point2f& a, const cv::Point2f& b) {
return a.y < b.y;
});
if (vertices[0].x > vertices[1].x)
std::swap(vertices[0], vertices[1]);
if (vertices[2].x > vertices[3].x)
std::swap(vertices[2], vertices[3]);
return vertices;
}
std::vector<cv::Point> CheckUtil::getLargestContourROI(const cv::Mat &binaryImg, bool &found) std::vector<cv::Point> CheckUtil::getLargestContourROI(const cv::Mat &binaryImg, bool &found)
{ {
std::vector<std::vector<cv::Point>> contours; std::vector<std::vector<cv::Point>> contours;
@ -481,6 +559,32 @@ int CheckUtil::CreateDir(const std::string &dir)
return ret; return ret;
} }
int CheckUtil::SaveImgAsync(const std::string &path, const cv::Mat &img)
{
if (path.empty() || img.empty())
{
return -1;
}
// 拷贝图像,避免异步线程执行时原图像被释放或修改
cv::Mat imgCopy = img.clone();
// 创建保存路径的父目录
std::string::size_type pos = path.find_last_of('/');
if (pos != std::string::npos)
{
std::string parentDir = path.substr(0, pos);
CheckUtil::CreateDir(parentDir);
}
// 启动后台线程异步存图
std::thread([path, imgCopy]() {
cv::imwrite(path, imgCopy);
}).detach();
return 0;
}
bool CheckUtil::bcalDis(cv::Point p1, cv::Point p2, int disT) bool CheckUtil::bcalDis(cv::Point p1, cv::Point p2, int disT)
{ {
double dis = std::sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); double dis = std::sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));

@ -3,7 +3,7 @@
* @Date: 2025-09-13 20:39:37 * @Date: 2025-09-13 20:39:37
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-09-23 14:29:12 * @LastEditTime: 2025-09-23 14:29:12
* @FilePath: /BOE_CELL_AOI_Detect/AlgorithmModule/src/CameraResult.cpp * @FilePath: /BOE_ZB_SOCKET_Detect/AlgorithmModule/src/CameraResult.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
#include "ImageAllResult.h" #include "ImageAllResult.h"
@ -94,11 +94,8 @@ int ImageAllResult::AddDetImage(std::shared_ptr<shareImage> p)
float fw = RESIZE_IMAGE_WIDTH * 1.0f / detImg.cols; float fw = RESIZE_IMAGE_WIDTH * 1.0f / detImg.cols;
sz.height = int(detImg.rows * fw); sz.height = int(detImg.rows * fw);
// 保持原图通道(ReJson 模式下 detImg 本身就是裁剪图),不再灰度转假彩
cv::resize(detImg, resultImg, sz); cv::resize(detImg, resultImg, sz);
if (resultImg.channels() == 1)
{
cv::cvtColor(resultImg, resultImg, cv::COLOR_GRAY2BGR);
}
result->resultimg = resultImg; result->resultimg = resultImg;

@ -147,6 +147,14 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
std::shared_ptr<CheckResult> m_CheckResult_shareP = pImageResult->result; // 返回结果 std::shared_ptr<CheckResult> m_CheckResult_shareP = pImageResult->result; // 返回结果
int qxNum = pDetResult->pQx_ErrorList->size(); int qxNum = pDetResult->pQx_ErrorList->size();
// 图像模糊: 检测阶段已在 AI 检测之前判定, 这里保证结果保持 NG(复测 ReJson 读取 json 后同样生效)
if (pDetResult->bBlurNG)
{
m_CheckResult_shareP->nresult = 1;
pImageLog->AddCheckstr(PrintLevel_0, "Blur Check",
"image blurred -> NG (variance %.2f)", pDetResult->fBlurScore);
}
// map<string, vector<iregionQxZoningInfo>> allQxZoning; // map<string, vector<iregionQxZoningInfo>> allQxZoning;
map<string, vector<singleQxZoningInfo>> allQxZoning; map<string, vector<singleQxZoningInfo>> allQxZoning;
@ -173,9 +181,9 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
std::string qx_wb = WHITE_BLCAK_Names[QX_whiteBLACK]; std::string qx_wb = WHITE_BLCAK_Names[QX_whiteBLACK];
pQxLog->AddCheckstr(PrintLevel_1, pQxLog->AddCheckstr(PrintLevel_1,
"", "qxidx: %d roi[%d %d %d %d] qx %d %s %s JudgArea %f hj %f len %f breadth %f energy %d region num %ld", "", "qxidx: %d roi[%d %d %d %d] qx %d %s %s JudgArea %f hj %f len %f breadth %f energy %d region num %ld detModel: %s",
qxidx, roi.x, roi.y, roi.width, roi.height, config_qx_type, qx_name.c_str(), qx_wb.c_str(), qxidx, roi.x, roi.y, roi.width, roi.height, config_qx_type, qx_name.c_str(), qx_wb.c_str(),
JudgArea, grayDis, flen, fbreadth, energy, QX_info->detRegionidxList.size()); JudgArea, grayDis, flen, fbreadth, energy, QX_info->detRegionidxList.size(), QX_info->strModelName.c_str());
cv::Point pCenter; cv::Point pCenter;
pCenter.x = roi.x + roi.width * 0.5; pCenter.x = roi.x + roi.width * 0.5;
@ -271,36 +279,17 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
allQxZoning[qx_name].push_back(singleQxZoningInfoTemp); allQxZoning[qx_name].push_back(singleQxZoningInfoTemp);
} }
// 黑缺陷 // 不区分亮暗缺陷: 参数(阈值)按正数配置; 实测灰阶/能量可能为负, 取绝对值后参与比较
if (QX_whiteBLACK == CONFIG_QX_WHITE) int energyAbs = std::abs(energy);
{ float grayDisAbs = std::abs(grayDis);
// pQxLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "param---", "qx :white");
if (hj >= 0 || Et >= 0)
{
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "param ", "Error param idx %d / %d qx white hj %f and energ %f >0",
j + 1, pParam->useNum, hj, Et);
continue;
}
Et = std::abs(Et);
hj = std::abs(hj);
}
else
{
// pQxLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "param---", "qx :blcak");
if (hj < 0 || Et < 0)
{
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "param ", "Error param idx %d / %d qx blcak hj %f and energ %f < 0",
j + 1, pParam->useNum, hj, Et);
continue;
}
}
Judge_Status = true; Judge_Status = true;
float detArea = JudgArea; float detArea = JudgArea;
if (energy >= Et && if (energyAbs >= Et &&
detArea >= At && detArea >= At &&
grayDis >= hj && grayDisAbs >= hj &&
flen >= Len && flen >= Len &&
fbreadth >= Breadth) fbreadth >= Breadth)
{ {
@ -330,15 +319,15 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "Area", "%s -> %f %s %f ", pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "Area", "%s -> %f %s %f ",
BOOL_TO_STR(detArea >= At), detArea, BOOL_TO_ThanLess(detArea >= At), At); BOOL_TO_STR(detArea >= At), detArea, BOOL_TO_ThanLess(detArea >= At), At);
} }
if (energy < Et) if (energyAbs < Et)
{ {
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "Energy", "%s -> %d %s %f ", pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "Energy", "%s -> %d %s %f ",
BOOL_TO_STR(energy >= Et), energy, BOOL_TO_ThanLess(energy >= Et), Et); BOOL_TO_STR(energyAbs >= Et), energyAbs, BOOL_TO_ThanLess(energyAbs >= Et), Et);
} }
if (grayDis < hj) if (grayDisAbs < hj)
{ {
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "HJ", "%s -> %f %s %f ", pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, "HJ", "%s -> %f %s %f ",
BOOL_TO_STR(grayDis >= hj), grayDis, BOOL_TO_ThanLess(grayDis > hj), hj); BOOL_TO_STR(grayDisAbs >= hj), grayDisAbs, BOOL_TO_ThanLess(grayDisAbs > hj), hj);
} }
if (flen < Len) if (flen < Len)
{ {
@ -381,6 +370,14 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
std::string resultType = "OK"; std::string resultType = "OK";
// PIN_PianYi, 不受区域参数控制;
if (!Judge_Status && config_qx_type == CONFIG_QX_NAME_PIN_PianYi)
{
pQxLog->AddCheckstr(PrintLevel_3, "qx info", " PIN_PianYi: region param not used -> report NG by base judge");
Judge_Status = true;
bNG_Status = true;
}
if (!Judge_Status) if (!Judge_Status)
{ {
QX_info->result = QX_RESULT_TYPE_NoJduge; QX_info->result = QX_RESULT_TYPE_NoJduge;
@ -503,13 +500,20 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
// CheckUtil::printROI(CutRoi, "CutRoi"); // CheckUtil::printROI(CutRoi, "CutRoi");
cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT); cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT);
if (pImageResult->detImg.channels() == 1) // 缺陷小图直接取自原图裁剪区(保持原图通道 -> 真实彩色),ReJson/异常时退回检测图
{
cv::cvtColor(pImageResult->detImg(CutRoi), tem.srcImg, cv::COLOR_GRAY2BGR);
}
else
{ {
tem.srcImg = pImageResult->detImg(CutRoi).clone(); const cv::Mat &srcImg = pImageResult->result->in_shareImage->img;
int nOffsetX = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.x : 0;
int nOffsetY = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.y : 0;
cv::Rect srcCanvasRoi(CutRoi.x + nOffsetX, CutRoi.y + nOffsetY, CutRoi.width, CutRoi.height);
if (!srcImg.empty() && CheckUtil::RoiInImg(srcCanvasRoi, srcImg))
{
tem.srcImg = srcImg(srcCanvasRoi).clone();
}
else
{
tem.srcImg = pImageResult->detImg(CutRoi).clone();
}
} }
cv::resize(tem.srcImg, tem.resizeImg, sz); cv::resize(tem.srcImg, tem.resizeImg, sz);
@ -559,9 +563,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
m_CheckResult_shareP->qxImageResult.push_back(tem); m_CheckResult_shareP->qxImageResult.push_back(tem);
pQxLog->bPrintStr = true; pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, " result ", " ---name: %s ---code: %s ---detModel: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), QX_info->strModelName.c_str(), tem.srcImgroi.x, tem.srcImgroi.y);
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, " result ", " ---name: %s ---code: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), tem.srcImgroi.x, tem.srcImgroi.y);
pQxLog->bPrintStr = false;
} }
else else
{ {
@ -574,6 +576,8 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
m_CheckResult_shareP->YS_ImageResult.push_back(tem); m_CheckResult_shareP->YS_ImageResult.push_back(tem);
// 疑似小图 也需要能追溯 是哪个模型检出的残点
pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, " YS result ", " ---name: %s ---code: %s ---detModel: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), QX_info->strModelName.c_str(), tem.srcImgroi.x, tem.srcImgroi.y);
} }
} }
} }
@ -622,13 +626,20 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
// CheckUtil::printROI(CutRoi, "CutRoi"); // CheckUtil::printROI(CutRoi, "CutRoi");
cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT); cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT);
if (pImageResult->detImg.channels() == 1) // 缺陷小图直接取自原图裁剪区(保持原图通道 -> 真实彩色),ReJson/异常时退回检测图
{ {
cv::cvtColor(pImageResult->detImg(CutRoi), tem.srcImg, cv::COLOR_GRAY2BGR); const cv::Mat &srcImg = pImageResult->result->in_shareImage->img;
} int nOffsetX = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.x : 0;
else int nOffsetY = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.y : 0;
{ cv::Rect srcCanvasRoi(CutRoi.x + nOffsetX, CutRoi.y + nOffsetY, CutRoi.width, CutRoi.height);
tem.srcImg = pImageResult->detImg(CutRoi).clone(); if (!srcImg.empty() && CheckUtil::RoiInImg(srcCanvasRoi, srcImg))
{
tem.srcImg = srcImg(srcCanvasRoi).clone();
}
else
{
tem.srcImg = pImageResult->detImg(CutRoi).clone();
}
} }
cv::resize(tem.srcImg, tem.resizeImg, sz); cv::resize(tem.srcImg, tem.resizeImg, sz);
@ -675,6 +686,8 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
m_CheckResult_shareP->qxImageResult.push_back(tem); m_CheckResult_shareP->qxImageResult.push_back(tem);
// YS 转 NG 的残点也需要能追溯 是哪个模型检出的
pQxLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, " YS to NG img ", " ---name: %s ---code: %s ---detModel: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), QX_info->strModelName.c_str(), tem.srcImgroi.x, tem.srcImgroi.y);
} }
} }
} }
@ -781,32 +794,9 @@ int ImageResultJudge::DrawResult(std::shared_ptr<ImageAllResult> pImageResult)
draw_str->txtP = cv::Point(draw_qx->cp.x + r, draw_qx->cp.y - r); draw_str->txtP = cv::Point(draw_qx->cp.x + r, draw_qx->cp.y - r);
draw_str->txtsize = 0.5; draw_str->txtsize = 0.5;
draw_str->txtbackgroundk = txtbackgroundk; draw_str->txtbackgroundk = txtbackgroundk;
cv::Rect srcroi = pqx->srcImgroi;
cv::Point srccp = cv::Point(srcroi.x + srcroi.width * .5, srcroi.y + srcroi.height * 0.5);
char buffer[128];
{ {
std::string text = std::to_string(pqx->idx) + ":" + pqx->strTypeName; // 只绘制 id:缺陷code (缺陷名可能中文乱码, 用code替代)
draw_str->strlist.push_back(text); std::string text = std::to_string(pqx->idx) + ":" + pqx->qx_Code;
}
{
sprintf(buffer, " center:%d %d", srccp.x, srccp.y);
std::string text = buffer;
draw_str->strlist.push_back(text);
}
{
sprintf(buffer, " len %0.2f A: %0.2f ", pqx->len, pqx->area);
std::string text = buffer;
draw_str->strlist.push_back(text);
}
{
sprintf(buffer, " breadth %0.2f A: %0.2f ", pqx->breadth, pqx->area);
std::string text = buffer;
draw_str->strlist.push_back(text);
}
{
sprintf(buffer, " E:%0.2f hj %0.2f", pqx->energy, pqx->hj);
std::string text = buffer;
draw_str->strlist.push_back(text); draw_str->strlist.push_back(text);
} }
sendTask(draw_str); sendTask(draw_str);
@ -1044,41 +1034,32 @@ int ImageResultJudge::ConfigTypeToResultType(int nconfigType)
{ {
switch (nconfigType) switch (nconfigType)
{ {
case CONFIG_QX_NAME_cell_aotudian: case CONFIG_QX_NAME_PIN_PoSun:
resultError_type = ERROR_TYPE_aotudian; resultError_type = ERROR_TYPE_PIN_PoSun;
break;
case CONFIG_QX_NAME_cell_other:
resultError_type = ERROR_TYPE_other;
break;
case CONFIG_QX_NAME_cell_line:
resultError_type = ERROR_TYPE_line;
break;
case CONFIG_QX_NAME_cell_zangwu:
resultError_type = ERROR_TYPE_zangwu;
break; break;
case CONFIG_QX_NAME_cell_edge: case CONFIG_QX_NAME_PIN_QueShi:
resultError_type = ERROR_TYPE_edge; resultError_type = ERROR_TYPE_PIN_QueShi;
break; break;
case CONFIG_QX_NAME_cell_ymhs: case CONFIG_QX_NAME_BTB_DuanLie:
resultError_type = ERROR_TYPE_ymhs; resultError_type = ERROR_TYPE_BTB_DuanLie;
break; break;
case CONFIG_QX_NAME_cell_dianzhuang: case CONFIG_QX_NAME_LianXi:
resultError_type = ERROR_TYPE_dianzhuang; resultError_type = ERROR_TYPE_LianXi;
break; break;
case CONFIG_QX_NAME_cell_posun: case CONFIG_QX_NAME_QueXi:
resultError_type = ERROR_TYPE_posun; resultError_type = ERROR_TYPE_QueXi;
break; break;
case CONFIG_QX_NAME_cell_xianwei: case CONFIG_QX_NAME_YiWu:
resultError_type = ERROR_TYPE_xianwei; resultError_type = ERROR_TYPE_YiWu;
break; break;
case CONFIG_QX_NAME_cell_shuizi: case CONFIG_QX_NAME_C_BianXing:
resultError_type = ERROR_TYPE_shuizi; resultError_type = ERROR_TYPE_C_BianXing;
break; break;
case CONFIG_QX_NAME_cell_danban: case CONFIG_QX_NAME_PIN_PianYi:
resultError_type = ERROR_TYPE_danban; resultError_type = ERROR_TYPE_PIN_PianYi;
break; break;
case CONFIG_QX_NAME_cell_fuchen: case CONFIG_QX_NAME_Panel:
resultError_type = ERROR_TYPE_fuchen; resultError_type = ERROR_TYPE_Panel;
break; break;
default: default:
@ -1119,11 +1100,27 @@ void ImageResultJudge::ThreadDraw()
while (!m_bExit) while (!m_bExit)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(0)); std::this_thread::sleep_for(std::chrono::milliseconds(0));
// 等待是否有任务
std::shared_ptr<ImageResultJudge::DrawInfo> draw = GetTask(); std::shared_ptr<ImageResultJudge::DrawInfo> draw;
// 取任务与 计数+1 必须在同一把锁内完成,
// 否则 WaiteDrawComplate 可能在任务已出队但尚未计数的间隙提前返回。
{
std::unique_lock<std::mutex> lock(m_task_mutex_);
m_task_cv_.wait(lock, [this]()
{ return !m_DrawInfoList.empty(); });
draw = std::move(m_DrawInfoList.front());
m_DrawInfoList.pop();
m_nDrawProcessingNum++;
}
// 把任务发送给对应的任务处理函数进行处理 // 把任务发送给对应的任务处理函数进行处理
DrawResultTask(draw); DrawResultTask(draw);
{
std::lock_guard<std::mutex> lock(m_task_mutex_);
m_nDrawProcessingNum--;
}
m_drawComplate_cv_.notify_all(); m_drawComplate_cv_.notify_all();
} }
} }
@ -1133,7 +1130,7 @@ int ImageResultJudge::DrawResultTask(std::shared_ptr<ImageResultJudge::DrawInfo>
if (task->type == Draw_circle) if (task->type == Draw_circle)
{ {
cv::circle(task->drawImg, task->cp, task->dr, task->color); cv::circle(task->drawImg, task->cp, task->dr, CheckUtil::GetDrawColor(task->drawImg, task->color));
if (task->txtbackgroundk) if (task->txtbackgroundk)
{ {
task->txtbackgroundk->txtDrawRoi.push_back(task->roi); task->txtbackgroundk->txtDrawRoi.push_back(task->roi);
@ -1141,7 +1138,7 @@ int ImageResultJudge::DrawResultTask(std::shared_ptr<ImageResultJudge::DrawInfo>
} }
if (task->type == Draw_rect) if (task->type == Draw_rect)
{ {
cv::rectangle(task->drawImg, task->roi, task->color); cv::rectangle(task->drawImg, task->roi, CheckUtil::GetDrawColor(task->drawImg, task->color));
if (task->txtbackgroundk) if (task->txtbackgroundk)
{ {
task->txtbackgroundk->txtDrawRoi.push_back(task->roi); task->txtbackgroundk->txtDrawRoi.push_back(task->roi);
@ -1205,7 +1202,7 @@ int ImageResultJudge::DrawResultTask(std::shared_ptr<ImageResultJudge::DrawInfo>
// 画文字 // 画文字
cv::putText(task->drawImg, txt, cv::Point(r.x, r.y + height), cv::putText(task->drawImg, txt, cv::Point(r.x, r.y + height),
fontFace, fontScale, task->color, thickness); fontFace, fontScale, CheckUtil::GetDrawColor(task->drawImg, task->color), thickness);
height += lineHeight; // 下一行 height += lineHeight; // 下一行
} }
// cv::rectangle(task->drawImg, rtxt, cv::Scalar(255, 0, 0)); // cv::rectangle(task->drawImg, rtxt, cv::Scalar(255, 0, 0));
@ -1213,7 +1210,7 @@ int ImageResultJudge::DrawResultTask(std::shared_ptr<ImageResultJudge::DrawInfo>
cv::Point p(r.x, r.y); cv::Point p(r.x, r.y);
if (task->cp.x - r.x > 30 || task->cp.y - r.y > 30) if (task->cp.x - r.x > 30 || task->cp.y - r.y > 30)
{ {
cv::line(task->drawImg, p, task->cp, cv::Scalar(80, 0, 200)); cv::line(task->drawImg, p, task->cp, CheckUtil::GetDrawColor(task->drawImg, cv::Scalar(80, 0, 200)));
} }
// cv::circle(task->drawImg, task->cp, 3, task->color); // cv::circle(task->drawImg, task->cp, 3, task->color);
@ -1224,10 +1221,13 @@ int ImageResultJudge::DrawResultTask(std::shared_ptr<ImageResultJudge::DrawInfo>
int ImageResultJudge::WaiteDrawComplate() int ImageResultJudge::WaiteDrawComplate()
{ {
// 等待 所有绘制任务真正执行完成。
// 旧实现只等待队列为空,而任务在取出时即已出队,绘制仍在后台线程进行,
// 调用方(结果存图/拷贝)可能与绘制线程并发读写同一张图片。
{ {
std::unique_lock<std::mutex> lock(m_task_mutex_); std::unique_lock<std::mutex> lock(m_task_mutex_);
m_drawComplate_cv_.wait(lock, [this]() m_drawComplate_cv_.wait(lock, [this]()
{ return m_DrawInfoList.empty(); }); { return m_DrawInfoList.empty() && m_nDrawProcessingNum == 0; });
} }
return 0; return 0;
} }

@ -3,7 +3,7 @@
* @Date: 2025-09-11 15:32:52 * @Date: 2025-09-11 15:32:52
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-09-16 09:40:36 * @LastEditTime: 2025-09-16 09:40:36
* @FilePath: /BOE_CELL_AOI_Detect/AlgorithmModule/src/ImageStorage.cpp * @FilePath: /BOE_ZB_SOCKET_Detect/AlgorithmModule/src/ImageStorage.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
#include "ImageStorage.h" #include "ImageStorage.h"

File diff suppressed because it is too large Load Diff

@ -5,52 +5,22 @@
#include <random> #include <random>
#include "CheckErrorCodeDefine.hpp" #include "CheckErrorCodeDefine.hpp"
int Pin_QX_Det::creatsavedir()
{
std::string curDate = CheckUtil::getCurrentDate();
if (curDate == m_strLastDate)
{
return 0;
}
m_strLastDate = curDate;
m_strSavePath_Pin = m_strRootPath_Pin + curDate + "/";
CheckUtil::CreateDir(m_strSavePath_Pin);
return 0;
return 0;
}
Pin_QX_Det::Pin_QX_Det(std::shared_ptr<DetLog> &log_ref) Pin_QX_Det::Pin_QX_Det(std::shared_ptr<DetLog> &log_ref)
: m_pdetlog(log_ref) : m_pdetlog(log_ref)
{ {
m_bInitialized = false;
m_bModelSucc = false;
m_bModel_Mark_Succ = false;
m_bshowimg = false;
m_strRootPath_Pin = "/home/aidlux/BOE/Pin/";
creatsavedir();
std::string m_strSavePath; std::string m_strSavePath;
m_pImageStorage = ImageStorage::getInstance();
AI_Factory = AIFactory::GetInstance(); AI_Factory = AIFactory::GetInstance();
m_strLastDate = "";
m_strRootPath_Pin = "/home/aidlux/BOE/Algin/Pin/";
m_pImageStorage = ImageStorage::getInstance();
creatsavedir();
} }
Pin_QX_Det::~Pin_QX_Det() Pin_QX_Det::~Pin_QX_Det()
{ {
} }
int Pin_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
{
// if (!pDetConfig->pBaseCheckFunction->edgeDet.bOpen)
// {
// return 0;
// }
m_bshowimg = false;
if (pDetConfig->bSaveResultImg)
{
m_bshowimg = true;
}
int Pin_QX_Det::GetPinMask(const cv::Mat &img, DetConfigResult *pDetConfig, Mat &mask)
{
std::shared_ptr<AIModel_Base> pSocket_Pin; std::shared_ptr<AIModel_Base> pSocket_Pin;
pSocket_Pin = AI_Factory->Pin_Loc; pSocket_Pin = AI_Factory->Pin_Loc;
cv::Size sz; cv::Size sz;
@ -62,7 +32,6 @@ int Pin_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
cv::resize(img, detImg, sz); cv::resize(img, detImg, sz);
int re = 0; int re = 0;
cv::Mat mask;
if (detImg.channels() != 1) if (detImg.channels() != 1)
{ {
cv::cvtColor(detImg, detImg, cv::COLOR_RGB2GRAY); cv::cvtColor(detImg, detImg, cv::COLOR_RGB2GRAY);
@ -71,27 +40,42 @@ int Pin_QX_Det::Detect(const cv::Mat &img, DetConfigResult *pDetConfig)
re = pSocket_Pin->AIDet(detImg, mask); re = pSocket_Pin->AIDet(detImg, mask);
if (re != 0) if (re != 0)
{ {
m_pdetlog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "AI_Edge_Algin ", "AICheck_Edge----error %d ", re); m_pdetlog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "AI_Pin ", "AICheck_Pin----error %d ", re);
int re123 = 100 + re; int re123 = 100 + re;
return re123; return re123;
} }
if (pDetConfig->pBaseCheckFunction->saveImg.bSaveAlginImg) // 参数 Align_Pin_Save 打开时, 保存Pin脚定位模型的输入/输出图(异步存图, 不阻塞检测)
// 输入图存未做通道转化(灰度化)之前的图; 文件名首位用产品ID
if (pDetConfig->pBaseCheckFunction != NULL && pDetConfig->pBaseCheckFunction->saveImg.bSavePinImg)
{ {
// 前缀用日期(年月日): 同一产品当天的所有图前缀一致; 文件已存在时在日期后追加序号, 不覆盖旧图
std::string strDatePre = CheckUtil::getCurrentDateYMD();
std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + pDetConfig->strProductID + "/KK/";
std::string strName = pDetConfig->strChannel + "_pin_out";
std::string strSavePath_out = CheckUtil::GetUniqueImgPath(strSaveDir, strDatePre, strName, ".png");
CheckUtil::SaveImgAsync(strSavePath_out, mask);
} }
if (pDetConfig->bSaveResultImg) if (pDetConfig->bSaveResultImg)
{ {
cv::imwrite(pDetConfig->strChannel +"_pin_" +"in.png", detImg); CheckUtil::SaveImgAsync(pDetConfig->strChannel +"_pin_" +"in.png", detImg);
cv::imwrite(pDetConfig->strChannel +"_pin_" +"out_mask.png", mask); CheckUtil::SaveImgAsync(pDetConfig->strChannel +"_pin_" +"out_mask.png", mask);
} }
if (m_bshowimg) return 0;
}
int Pin_QX_Det::creatsavedir()
{
std::string curDate = CheckUtil::getCurrentDate();
if (curDate == m_strLastDate)
{ {
cv::cvtColor(img, showimg, cv::COLOR_GRAY2BGR); return 0;
} }
m_strLastDate = curDate;
m_strSavePath_Pin = m_strRootPath_Pin + curDate + "/";
CheckUtil::CreateDir(m_strSavePath_Pin);
return 0; return 0;
} }

@ -3,7 +3,7 @@
* @Date: 2025-09-13 19:26:19 * @Date: 2025-09-13 19:26:19
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-09-17 18:10:38 * @LastEditTime: 2025-09-17 18:10:38
* @FilePath: /BOE_CELL_AOI_Detect/AlgorithmModule/src/Product.cpp * @FilePath: /BOE_ZB_SOCKET_Detect/AlgorithmModule/src/Product.cpp
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
* *
*/ */

@ -32,11 +32,13 @@ set(ModuleName "")
# 自定义安装前缀 # 自定义安装前缀
set(CMAKE_INSTALL_PREFIX /usr/local/polet CACHE PATH "Install path prefix" FORCE) set(CMAKE_INSTALL_PREFIX /usr/local/polet CACHE PATH "Install path prefix" FORCE)
set(HEADER_FILES include/ConfigBase.h) set(HEADER_FILES include/ConfigBase.h)
# 库文件添加 _socket 后缀
set_target_properties(Config PROPERTIES OUTPUT_NAME "Config_socket")
# 安装动态库 # 安装动态库
install(TARGETS Config install(TARGETS Config
LIBRARY DESTINATION lib # 安装到 CMAKE_INSTALL_PREFIX/lib LIBRARY DESTINATION lib # 安装到 CMAKE_INSTALL_PREFIX/lib
ARCHIVE DESTINATION lib/static ARCHIVE DESTINATION lib/static
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
PUBLIC_HEADER DESTINATION include) # 安装到 CMAKE_INSTALL_PREFIX/include PUBLIC_HEADER DESTINATION include) # 安装到 CMAKE_INSTALL_PREFIX/include
# 安装头文件 # 安装头文件(添加 _socket 后缀)
install(FILES ${HEADER_FILES} DESTINATION include) install(FILES include/ConfigBase.h DESTINATION include RENAME ConfigBase_socket.h)

@ -39,35 +39,31 @@ static const std::string WHITE_BLCAK_Names[] =
// 检测缺陷的种类 // 检测缺陷的种类
enum CONFIG_QX_NAME_ enum CONFIG_QX_NAME_
{ {
CONFIG_QX_NAME_cell_aotudian, // 分类 凹凸点 CONFIG_QX_NAME_PIN_PoSun, // 分类 PIN破损
CONFIG_QX_NAME_cell_other, // 分类 其他 CONFIG_QX_NAME_PIN_QueShi, // 分类 PIN缺失
CONFIG_QX_NAME_cell_line, // 分类 线状 CONFIG_QX_NAME_BTB_DuanLie, // 分类 BTB断裂
CONFIG_QX_NAME_cell_zangwu, // 分类 脏污 CONFIG_QX_NAME_LianXi, // 分类 连锡
CONFIG_QX_NAME_cell_edge, // 分类 边缘 CONFIG_QX_NAME_QueXi, // 分类 缺锡
CONFIG_QX_NAME_cell_ymhs, // 分类 异物 CONFIG_QX_NAME_HunLiao, // 分类 混料
CONFIG_QX_NAME_cell_dianzhuang, // 分类 点状 CONFIG_QX_NAME_YiWu, // 分类 异物
CONFIG_QX_NAME_cell_posun, // 分类 破损 CONFIG_QX_NAME_C_BianXing, // 分类 C型卡口异常
CONFIG_QX_NAME_cell_xianwei, // 分类 纤维 CONFIG_QX_NAME_PIN_PianYi, // 分类 PIN偏移
CONFIG_QX_NAME_cell_shuizi, // 分类 水渍 CONFIG_QX_NAME_Panel, // 分类 Panel
CONFIG_QX_NAME_cell_danban, // 分类 淡斑
CONFIG_QX_NAME_cell_fuchen, // 分类 浮尘
CONFIG_QX_NAME_count, CONFIG_QX_NAME_count,
}; };
// 缺陷项对应在参数中的名称 // 缺陷项对应在参数中的名称
static std::vector<std::string> CONFIG_QX_NAME_Names = static std::vector<std::string> CONFIG_QX_NAME_Names =
{ {
"aotudian", "PIN_PoSun",
"other", "PIN_QueShi",
"line", "BTB_DuanLie",
"zangwu", "QueXi",
"edge", "LianXi",
"ymhs", "HunLiao",
"dianzhuang", "YiWu",
"posun", "C_BianXing",
"xianwei", "PIN_PianYi",
"shuizi", "Panel",
"danban",
"fuchen",
}; };
// 分析类型 // 分析类型
@ -1018,52 +1014,56 @@ struct ChannelCheckFunction
}; };
// 基础检测功能 mark // 基础检测功能 mark
struct Base_Function_MarkLine struct Base_Function_ALign
{ {
bool bOpen; // 是否开启 bool bOpen; // 是否开启
bool bDraw; // 是否绘制 float angle_diff;
bool badapt_region; // 是否自适应区域 float x_offset;
cv::Rect productROI; float y_offset;
std::vector<cv::Point> region; float len;
cv::Point mark_local_1; float width;
cv::Point mark_local_2; float len_diff;
float width_diff;
Base_Function_MarkLine()
Base_Function_ALign()
{ {
Init(); Init();
} }
void Init() void Init()
{ {
bOpen = false; bOpen = false;
bDraw = false; angle_diff = 0.0;
badapt_region = false; x_offset = 0.0;
mark_local_1 = cv::Point(0, 0); y_offset = 0.0;
mark_local_2 = cv::Point(0, 0); len = 0.0;
productROI = cv::Rect(0, 0, 0, 0); width = 0.0;
region.clear(); len_diff = 0.0;
width_diff = 0.0;
} }
void copy(Base_Function_MarkLine tem) void copy(Base_Function_ALign tem)
{ {
this->bOpen = tem.bOpen; this->bOpen = tem.bOpen;
this->region.assign(tem.region.begin(), tem.region.end()); this->angle_diff = tem.angle_diff;
this->productROI = tem.productROI; this->x_offset = tem.x_offset;
this->bDraw = tem.bDraw; this->y_offset = tem.y_offset;
this->badapt_region = tem.badapt_region; this->len = tem.len;
this->mark_local_1 = tem.mark_local_1; this->width = tem.width;
this->mark_local_2 = tem.mark_local_2; this->len_diff = tem.len_diff;
this->width_diff = tem.width_diff;
} }
void print(std::string str) void print(std::string str)
{ {
printf("%s>>bOpen %d bDraw %d badapt_region %d mark1 [%d %d] mark2 [%d %d] \n", str.c_str(), printf("%s>>bOpen %d angle_diff %f x_offset %f y_offset %f len %f width %f len_diff %f width_diff %f\n", str.c_str(),
bOpen, bDraw, badapt_region,mark_local_1.x, mark_local_1.y, mark_local_2.x, mark_local_2.y); bOpen, angle_diff, x_offset, y_offset, len, width, len_diff, width_diff);
} }
std::string GetInfo(std::string str) std::string GetInfo(std::string str)
{ {
char buffer[256]; char buffer[256];
sprintf(buffer, "%s>>bOpen %d bDraw %d badapt_region %d mark1 [%d %d] mark2 [%d %d] \n", str.c_str(), sprintf(buffer, "%s>>bOpen %d angle_diff %f x_offset %f y_offset %f len %f width %f len_diff %f width_diff %f\n", str.c_str(),
bOpen, bDraw, badapt_region,mark_local_1.x, mark_local_1.y, mark_local_2.x, mark_local_2.y); bOpen, angle_diff, x_offset, y_offset, len, width, len_diff, width_diff);
std::string str123 = buffer; std::string str123 = buffer;
return str123; return str123;
} }
@ -1074,9 +1074,10 @@ struct Base_Function_SaveImg
{ {
bool bOpen; // 是否开启 bool bOpen; // 是否开启
bool bSaveMarkImg; // mark 存图
bool bSaveClsImg; // 分类 存图 bool bSaveClsImg; // 分类 存图
bool bSaveAlginImg; // 分类 存图 bool bSaveAlginImg; // 定位 存图
bool bSavePinImg; // pin 存图
bool bSaveAIDetImg; // AI 检测残点图 存图(每个模型单独一级目录)
Base_Function_SaveImg() Base_Function_SaveImg()
{ {
@ -1085,29 +1086,31 @@ struct Base_Function_SaveImg
void Init() void Init()
{ {
bOpen = false; bOpen = false;
bSaveMarkImg = false; bSavePinImg = false;
bSaveClsImg = false; bSaveClsImg = false;
bSaveAlginImg = false; bSaveAlginImg = false;
bSaveAIDetImg = false;
} }
void copy(Base_Function_SaveImg tem) void copy(Base_Function_SaveImg tem)
{ {
this->bOpen = tem.bOpen; this->bOpen = tem.bOpen;
this->bSaveMarkImg = tem.bSaveMarkImg; this->bSavePinImg = tem.bSavePinImg;
this->bSaveClsImg = tem.bSaveClsImg; this->bSaveClsImg = tem.bSaveClsImg;
this->bSaveAlginImg = tem.bSaveAlginImg; this->bSaveAlginImg = tem.bSaveAlginImg;
this->bSaveAIDetImg = tem.bSaveAIDetImg;
} }
void print(std::string str) void print(std::string str)
{ {
printf("%s>>bOpen %d bSaveMarkImg %d bSaveClsImg %d bSaveAlginImg %d \n", str.c_str(), printf("%s>>bOpen %d bSavePinImg %d bSaveClsImg %d bSaveAlginImg %d bSaveAIDetImg %d \n", str.c_str(),
bOpen, bSaveMarkImg, bSaveClsImg, bSaveAlginImg); bOpen, bSavePinImg, bSaveClsImg, bSaveAlginImg, bSaveAIDetImg);
} }
std::string GetInfo(std::string str) std::string GetInfo(std::string str)
{ {
char buffer[256]; char buffer[256];
sprintf(buffer, "%s>>bOpen %d bSaveMarkImg %d bSaveClsImg %d bSaveAlginImg %d \n", str.c_str(), sprintf(buffer, "%s>>bOpen %d bSavePinImg %d bSaveClsImg %d bSaveAlginImg %d bSaveAIDetImg %d \n", str.c_str(),
bOpen, bSaveMarkImg, bSaveClsImg, bSaveAlginImg); bOpen, bSavePinImg, bSaveClsImg, bSaveAlginImg, bSaveAIDetImg);
std::string str123 = buffer; std::string str123 = buffer;
return str123; return str123;
} }
@ -1152,6 +1155,78 @@ struct Big_NG
return str123; return str123;
} }
}; };
struct Base_Function_Merge_QueXi
{
bool bOpen;
Base_Function_Merge_QueXi()
{
Init();
}
void Init()
{
bOpen = false;
}
void copy(Base_Function_Merge_QueXi tem)
{
this->bOpen = tem.bOpen;
}
void print(std::string str)
{
printf("%s>>bOpen %d \n", str.c_str(),
bOpen);
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%s>>bOpen %d \n", str.c_str(),
bOpen);
std::string str123 = buffer;
return str123;
}
};
// 基础检测功能 图像模糊度(清晰度)检测
// 执行时机: AI 残点检测之前, 对送检图(检测图 detImg)计算模糊度, 模糊图片可直接判 NG
// 界面参数: itemCode = "Blur_Detect", form.Blur_Config = { bNG, blur_threshold }
struct Base_Function_Blur
{
bool bOpen; // 是否开启
bool bNG; // 模糊图片是否直接判 NG
float fThreshold; // 模糊度阈值(拉普拉斯方差下限): 方差低于该值判为模糊
Base_Function_Blur()
{
Init();
}
void Init()
{
bOpen = true; // 默认开启(参数里没有该配置项时也做模糊判定)
bNG = true; // 默认模糊即 NG
fThreshold = 800.0f; // 默认阈值(实测: 清晰图 ~8100, sigma=3 模糊 ~880, sigma=5 模糊 ~250)
}
void copy(Base_Function_Blur tem)
{
this->bOpen = tem.bOpen;
this->bNG = tem.bNG;
this->fThreshold = tem.fThreshold;
}
void print(std::string str)
{
printf("%s>>bOpen %d bNG %d fThreshold %f \n", str.c_str(), bOpen, bNG, fThreshold);
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%s>>bOpen %d bNG %d fThreshold %f \n", str.c_str(), bOpen, bNG, fThreshold);
std::string str123 = buffer;
return str123;
}
};
// 基础检测功能 大缺陷 NG // 基础检测功能 大缺陷 NG
struct Base_Function_BigNG struct Base_Function_BigNG
{ {
@ -1206,91 +1281,49 @@ struct Base_Function_BigNG
} }
}; };
// 基础检测功能 CELL AOI 边缘缺陷检测 // 基础检测功能 CELL AOI 边缘缺陷检测
struct Base_Function_Edge_Det struct Base_Function_Pin_Det
{ {
bool bOpen; // 是否开启 bool bOpen; // 是否开启
int Search_threshold; // 搜索边缘阈值 float pin_x_offset; // pin x 偏移
int Det_threshold; // 缺陷检测阈值 float pin_y_offset; // pin y 偏移
int Det_Range; // 检测范围 // "line_fitting" = 新方案(直接分析检测Pin: 分两排拟合直线判偏移 + 相邻间距判缺失, 不做模板对比)
int QX_Widht_min; // 缺陷宽度-最小 // "BTB_Marix" = 旧方案(内定位仿射 + 模板Pin对比)
int QX_Widht_max; // "Pin_Matrix" = 模板Pin对比方案, 投影矩阵由 Pin 自身拟合(上下两排分别计算, 不做二次偏移矫正)
int QX_Height_min; std::string Det_Method; // pin 检测方案
int QX_Height_max;
bool bDrawResult; // 是否绘制结果
bool queJiao_LU_Open; Base_Function_Pin_Det()
bool queJiao_RU_Open;
bool queJiao_LD_Open;
bool queJiao_RD_Open;
int queJiao_width;
int queJiao_height;
bool bDrawRoi;
cv::Rect draw_ROI;
std::vector<cv::Point> region;
bool bDrawResult; // 是否绘制结果
Base_Function_Edge_Det()
{ {
Init(); Init();
} }
void Init() void Init()
{ {
bOpen = false; bOpen = false;
Search_threshold = 30; pin_x_offset = 0;
Det_threshold = 30; pin_y_offset = 0;
Det_Range = 100; Det_Method = "BTB_Marix";
QX_Widht_min = 10;
QX_Widht_max = 100;
QX_Height_min = 10;
QX_Height_max = 100;
queJiao_LU_Open = false;
queJiao_RU_Open = false;
queJiao_LD_Open = false;
queJiao_RD_Open = false;
queJiao_width = 50;
queJiao_height = 50;
bDrawRoi = false;
draw_ROI = cv::Rect(0, 0, 0, 0);
region.clear();
region.erase(region.begin(), region.end());
bDrawResult = false; bDrawResult = false;
} }
void copy(Base_Function_Edge_Det tem) void copy(Base_Function_Pin_Det tem)
{ {
this->bOpen = tem.bOpen; this->bOpen = tem.bOpen;
this->Search_threshold = tem.Search_threshold; this->pin_x_offset = tem.pin_x_offset;
this->Det_threshold = tem.Det_threshold; this->pin_y_offset = tem.pin_y_offset;
this->Det_Range = tem.Det_Range; this->Det_Method = tem.Det_Method;
this->QX_Widht_min = tem.QX_Widht_min;
this->QX_Widht_max = tem.QX_Widht_max;
this->QX_Height_min = tem.QX_Height_min;
this->QX_Height_max = tem.QX_Height_max;
this->queJiao_LU_Open = tem.queJiao_LU_Open;
this->queJiao_RU_Open = tem.queJiao_RU_Open;
this->queJiao_LD_Open = tem.queJiao_LD_Open;
this->queJiao_RD_Open = tem.queJiao_RD_Open;
this->queJiao_width = tem.queJiao_width;
this->queJiao_height = tem.queJiao_height;
this->region.assign(tem.region.begin(), tem.region.end());
this->draw_ROI = tem.draw_ROI;
this->bDrawRoi = tem.bDrawRoi;
this->bDrawResult = tem.bDrawResult; this->bDrawResult = tem.bDrawResult;
} }
void print(std::string str) void print(std::string str)
{ {
printf("%s>>bOpen %d Search_threshold %d Det_threshold %d Det_Range %d QX_Widht [%d %d] QX_Height [%d %d]\n", str.c_str(), printf("%s>>bOpen %d pin_x_offset %f pin_y_offset %f Det_Method %s bDrawResult %d\n", str.c_str(),
bOpen, Search_threshold, Det_threshold, Det_Range, QX_Widht_min, QX_Widht_max, QX_Height_min, QX_Height_max); bOpen, pin_x_offset, pin_y_offset, Det_Method.c_str(), bDrawResult);
} }
std::string GetInfo(std::string str) std::string GetInfo(std::string str)
{ {
char buffer[256]; char buffer[256];
sprintf(buffer, "%s>>bOpen %d Search_threshold %d Det_threshold %d Det_Range %d QX_Widht [%d %d] QX_Height [%d %d]\n", str.c_str(), sprintf(buffer, "%s>>bOpen %d pin_x_offset %f pin_y_offset %f Det_Method %s bDrawResult %d\n", str.c_str(),
bOpen, Search_threshold, Det_threshold, Det_Range, QX_Widht_min, QX_Widht_max, QX_Height_min, QX_Height_max); bOpen, pin_x_offset, pin_y_offset, Det_Method.c_str(), bDrawResult);
std::string str123 = buffer; std::string str123 = buffer;
return str123; return str123;
} }
@ -1298,9 +1331,11 @@ struct Base_Function_Edge_Det
// 基础检测功能 // 基础检测功能
struct BaseCheckFunction struct BaseCheckFunction
{ {
Base_Function_MarkLine markLine; Base_Function_ALign ALign_Config;
Base_Function_Edge_Det edgeDet; Base_Function_Pin_Det pinDet;
Base_Function_SaveImg saveImg; Base_Function_SaveImg saveImg;
Base_Function_Merge_QueXi Merge_QueXi;
Base_Function_Blur blurDet;
Base_Function_BigNG bigNG; Base_Function_BigNG bigNG;
BaseCheckFunction() BaseCheckFunction()
{ {
@ -1308,32 +1343,40 @@ struct BaseCheckFunction
} }
void Init() void Init()
{ {
markLine.Init(); ALign_Config.Init();
edgeDet.Init(); pinDet.Init();
saveImg.Init(); saveImg.Init();
Merge_QueXi.Init();
blurDet.Init();
bigNG.Init(); bigNG.Init();
} }
void copy(BaseCheckFunction tem) void copy(BaseCheckFunction tem)
{ {
this->markLine.copy(tem.markLine); this->ALign_Config.copy(tem.ALign_Config);
this->edgeDet.copy(tem.edgeDet); this->pinDet.copy(tem.pinDet);
this->saveImg.copy(tem.saveImg); this->saveImg.copy(tem.saveImg);
this->Merge_QueXi.copy(tem.Merge_QueXi);
this->blurDet.copy(tem.blurDet);
this->bigNG.copy(tem.bigNG); this->bigNG.copy(tem.bigNG);
} }
void print(std::string str) void print(std::string str)
{ {
printf("******* %s *********\n", str.c_str()); printf("******* %s *********\n", str.c_str());
markLine.print("markLine"); ALign_Config.print("ALign_Config");
edgeDet.print("edgeDet"); pinDet.print("pinDet");
saveImg.print("saveImg"); saveImg.print("saveImg");
Merge_QueXi.print("Merge_QueXi");
blurDet.print("blurDet");
bigNG.print("bigNG"); bigNG.print("bigNG");
} }
std::string GetInfo(std::string str) std::string GetInfo(std::string str)
{ {
std::string str123 = ""; std::string str123 = "";
str123 += markLine.GetInfo("markLine"); str123 += ALign_Config.GetInfo("ALign_Config");
str123 += edgeDet.GetInfo("edgeDet"); str123 += pinDet.GetInfo("pinDet");
str123 += saveImg.GetInfo("saveImg"); str123 += saveImg.GetInfo("saveImg");
str123 += Merge_QueXi.GetInfo("Merge_QueXi");
str123 += blurDet.GetInfo("blurDet");
str123 += bigNG.GetInfo("bigNG"); str123 += bigNG.GetInfo("bigNG");
// str123 += "\n"; // str123 += "\n";
return str123; return str123;

@ -3,7 +3,7 @@
* @Date: 2025-09-11 15:32:54 * @Date: 2025-09-11 15:32:54
* @LastEditors: xiewenji 527774126@qq.com * @LastEditors: xiewenji 527774126@qq.com
* @LastEditTime: 2025-09-13 18:49:40 * @LastEditTime: 2025-09-13 18:49:40
* @FilePath: /BOE_CELL_AOI_Detect/ConfigModule/include/ConfigBase.h * @FilePath: /BOE_ZB_SOCKET_Detect/ConfigModule/include/ConfigBase.h
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
#ifndef ConfigBase_H_ #ifndef ConfigBase_H_

@ -20,37 +20,33 @@ ConfigManager::~ConfigManager()
{ {
} }
std::vector<ReadFlawCode> m_FlawCodeList;
std::vector<std::string> QX_Result_Names = std::vector<std::string> QX_Result_Names =
{ {
"OK", "OK",
"aotudian", "PIN_PoSun",
"other", "PIN_QueShi",
"line", "BTB_DuanLie",
"zangwu", "LianXi",
"edge", "HunLiao",
"ymhs", "YiWu",
"dianzhuang", "PianYi",
"posun", "C_BianXing",
"xianwei", "Panel",
"shuizi",
"danban",
"fuchen",
}; };
std::vector<std::string> QX_Result_Code = std::vector<std::string> QX_Result_Code =
{ {
"P0000", "P0000",
"MA507", "M200224",
"MA508", "M200223",
"MA506", "M200225",
"MA504", "M200227",
"MA503", "M200228",
"MA502", "M200221",
"MA505", "M200229",
"MA501", "M200226",
"P0001", "M101010",
"P0002",
"P0003",
"P0004",
}; };
int ReadFlawCodeConfig(std::string json_path) int ReadFlawCodeConfig(std::string json_path)
@ -167,7 +163,7 @@ int ConfigManager::UpdateConfig()
ReadFlawCodeConfig(defect_list_file); ReadFlawCodeConfig(defect_list_file);
bool bFileName = false; bool bFileName = false;
// std::regex pattern(R"(param_[0-9]\.json)"); // std::regex pattern(R"(param_[0-9]\.json)");
std::regex pattern(R"(param_(\d+|left|right)\.json)"); std::regex pattern(R"(param_.*\.json)");
if (!fs::exists(m_strConfigRootPath)) if (!fs::exists(m_strConfigRootPath))
{ {
std::cerr << "目录不存在: " << m_strConfigRootPath << std::endl; std::cerr << "目录不存在: " << m_strConfigRootPath << std::endl;

@ -775,78 +775,42 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
auto value_f = value; auto value_f = value;
// std::cout << value_f << std::endl; // std::cout << value_f << std::endl;
// getchar(); // getchar();
_config.markLine.bOpen = value_f["isOpen"].asBool(); _config.ALign_Config.bOpen = value_f["isOpen"].asBool();
if (_config.markLine.bOpen) if (_config.ALign_Config.bOpen)
{ {
if (value_f["form"]["ALign_Config"]["angle_diff"])
{ {
auto crop_roi = value_f["form"]["ALign_Config"]["Product_ROI"]; _config.ALign_Config.angle_diff = value_f["form"]["ALign_Config"]["angle_diff"].asFloat();
// 2、读取区域点
{
auto value_region = value_f["form"]["ALign_Config"]["Product_ROI"];
if (value_region.isArray())
{
for (int idx = 0; idx < value_region.size(); idx++)
{
cv::Point p;
p.x = value_region[idx][0].asInt();
p.y = value_region[idx][1].asInt();
_config.markLine.region.emplace_back(p);
}
if (_config.markLine.region.size() > 0)
{
_config.markLine.productROI = boundingRect(_config.markLine.region);
}
}
}
// printf(" pointArry1 size %d \n", function.f_ShieldRegion.pointArry1.size());
} }
if (value_f["form"]["ALign_Config"]["bDraw"]) if (value_f["form"]["ALign_Config"]["x_offset"])
{ {
_config.markLine.bDraw = value_f["form"]["ALign_Config"]["bDraw"].asBool(); _config.ALign_Config.x_offset = value_f["form"]["ALign_Config"]["x_offset"].asFloat();
} }
if (value_f["form"]["ALign_Config"]["badapt_region"]) if (value_f["form"]["ALign_Config"]["y_offset"])
{ {
_config.markLine.badapt_region = value_f["form"]["ALign_Config"]["badapt_region"].asBool(); _config.ALign_Config.y_offset = value_f["form"]["ALign_Config"]["y_offset"].asFloat();
} }
{
auto value_circl = value_f["form"]["ALign_Config"]["mark_1_Local"]["path"];
if (value_circl.isArray())
{
if (value_circl[0][0]) if (value_f["form"]["ALign_Config"]["len"])
{ {
_config.markLine.mark_local_1.x = value_circl[0][0].asInt(); _config.ALign_Config.len = value_f["form"]["ALign_Config"]["len"].asFloat();
}
if (value_circl[0][1])
{
_config.markLine.mark_local_1.y = value_circl[0][1].asInt();
}
}
} }
if (value_f["form"]["ALign_Config"]["width"])
{ {
auto value_circl = value_f["form"]["ALign_Config"]["mark_2_Local"]["path"]; _config.ALign_Config.width = value_f["form"]["ALign_Config"]["width"].asFloat();
if (value_circl.isArray()) }
{ if (value_f["form"]["ALign_Config"]["len_diff"])
{
if (value_circl[0][0]) _config.ALign_Config.len_diff = value_f["form"]["ALign_Config"]["len_diff"].asFloat();
{ }
_config.markLine.mark_local_2.x = value_circl[0][0].asInt(); if (value_f["form"]["ALign_Config"]["width_diff"])
} {
if (value_circl[0][1]) _config.ALign_Config.width_diff = value_f["form"]["ALign_Config"]["width_diff"].asFloat();
{
_config.markLine.mark_local_2.y = value_circl[0][1].asInt();
}
}
} }
// _config.markLine.print("dfe");
// getchar();
} }
else else
{ {
_config.markLine.Init(); _config.ALign_Config.Init();
} }
} }
if ("Pin_QX_Detect" == strCode) if ("Pin_QX_Detect" == strCode)
@ -854,111 +818,35 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
auto value_f = value; auto value_f = value;
// std::cout << value_f << std::endl; // std::cout << value_f << std::endl;
// getchar(); // getchar();
_config.edgeDet.bOpen = value_f["isOpen"].asBool(); _config.pinDet.bOpen = value_f["isOpen"].asBool();
if (_config.edgeDet.bOpen) if (_config.pinDet.bOpen)
{ {
if (value_f["form"]["Search_Config"]["Search_threshold"]) if (value_f["form"]["Det_Config"]["Det_Method"])
{
_config.edgeDet.Search_threshold = value_f["form"]["Search_Config"]["Search_threshold"].asInt();
}
if (value_f["form"]["Detect_Config"]["Det_threshold"])
{
_config.edgeDet.Det_threshold = value_f["form"]["Detect_Config"]["Det_threshold"].asInt();
}
if (value_f["form"]["Detect_Config"]["Det_Range"])
{
_config.edgeDet.Det_Range = value_f["form"]["Detect_Config"]["Det_Range"].asInt();
}
if (value_f["form"]["Detect_Config"]["QX_Widht_min"])
{
_config.edgeDet.QX_Widht_min = value_f["form"]["Detect_Config"]["QX_Widht_min"].asInt();
}
if (value_f["form"]["Detect_Config"]["QX_Widht_max"])
{
_config.edgeDet.QX_Widht_max = value_f["form"]["Detect_Config"]["QX_Widht_max"].asInt();
}
if (value_f["form"]["Detect_Config"]["QX_Height_min"])
{
_config.edgeDet.QX_Height_min = value_f["form"]["Detect_Config"]["QX_Height_min"].asInt();
}
if (value_f["form"]["Detect_Config"]["QX_Height_max"])
{
_config.edgeDet.QX_Height_max = value_f["form"]["Detect_Config"]["QX_Height_max"].asInt();
}
///============================================
if (value_f["form"]["Jiao_Config"]["LU_Jiao"])
{
_config.edgeDet.queJiao_LU_Open = value_f["form"]["Jiao_Config"]["LU_Jiao"].asBool();
}
if (value_f["form"]["Jiao_Config"]["RU_Jiao"])
{
_config.edgeDet.queJiao_RU_Open = value_f["form"]["Jiao_Config"]["RU_Jiao"].asBool();
}
if (value_f["form"]["Jiao_Config"]["LD_Jiao"])
{
_config.edgeDet.queJiao_LD_Open = value_f["form"]["Jiao_Config"]["LD_Jiao"].asBool();
}
if (value_f["form"]["Jiao_Config"]["RD_Jiao"])
{
_config.edgeDet.queJiao_RD_Open = value_f["form"]["Jiao_Config"]["RD_Jiao"].asBool();
}
if (value_f["form"]["Jiao_Config"]["Jiao_Width"])
{ {
_config.edgeDet.queJiao_width = value_f["form"]["Jiao_Config"]["Jiao_Width"].asInt(); _config.pinDet.Det_Method = value_f["form"]["Det_Config"]["Det_Method"].asString();
} }
if (value_f["form"]["Det_Config"]["pin_x_offset"])
if (value_f["form"]["Jiao_Config"]["Jiao_Height"])
{ {
_config.edgeDet.queJiao_height = value_f["form"]["Jiao_Config"]["Jiao_Height"].asInt(); _config.pinDet.pin_x_offset = value_f["form"]["Det_Config"]["pin_x_offset"].asFloat();
} }
if (value_f["form"]["Det_Config"]["pin_y_offset"])
if (value_f["form"]["Draw_Config"]["Open"])
{ {
_config.edgeDet.bDrawRoi = value_f["form"]["Draw_Config"]["Open"].asBool(); _config.pinDet.pin_y_offset = value_f["form"]["Det_Config"]["pin_y_offset"].asFloat();
}
// 2、读取区域点
{
auto value_region = value_f["form"]["Draw_Config"]["Edge_ROI"];
if (value_region.isArray())
{
for (int idx = 0; idx < value_region.size(); idx++)
{
cv::Point p;
p.x = value_region[idx][0].asInt();
p.y = value_region[idx][1].asInt();
_config.edgeDet.region.emplace_back(p);
}
if (_config.edgeDet.region.size() > 0)
{
_config.edgeDet.draw_ROI = boundingRect(_config.edgeDet.region);
}
}
} }
if (value_f["form"]["otherConfig"]["bDrawResult"]) if (value_f["form"]["otherConfig"]["bDrawResult"])
{ {
_config.edgeDet.bDrawResult = value_f["form"]["otherConfig"]["bDrawResult"].asBool(); _config.pinDet.bDrawResult = value_f["form"]["otherConfig"]["bDrawResult"].asBool();
} }
} }
else else
{ {
_config.edgeDet.Init(); _config.pinDet.Init();
} }
// _config.edgeDet.print("edgeDet"); // _config.pinDet.print("pinDet");
// getchar(); // getchar();
} }
if ("Det_Image_Save" == strCode) if ("KK_Image_Save" == strCode)
{ {
auto value_f = value; auto value_f = value;
// std::cout << value_f << std::endl; // std::cout << value_f << std::endl;
@ -967,10 +855,6 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
if (_config.saveImg.bOpen) if (_config.saveImg.bOpen)
{ {
if (value_f["form"]["SaveImg_Config"]["Mark_Save"])
{
_config.saveImg.bSaveMarkImg = value_f["form"]["SaveImg_Config"]["Mark_Save"].asBool();
}
if (value_f["form"]["SaveImg_Config"]["Cls_Save"]) if (value_f["form"]["SaveImg_Config"]["Cls_Save"])
{ {
_config.saveImg.bSaveClsImg = value_f["form"]["SaveImg_Config"]["Cls_Save"].asBool(); _config.saveImg.bSaveClsImg = value_f["form"]["SaveImg_Config"]["Cls_Save"].asBool();
@ -979,147 +863,170 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
{ {
_config.saveImg.bSaveAlginImg = value_f["form"]["SaveImg_Config"]["Align_Big_Save"].asBool(); _config.saveImg.bSaveAlginImg = value_f["form"]["SaveImg_Config"]["Align_Big_Save"].asBool();
} }
if (value_f["form"]["SaveImg_Config"]["Align_Pin_Save"])
{
_config.saveImg.bSavePinImg = value_f["form"]["SaveImg_Config"]["Align_Pin_Save"].asBool();
}
if (value_f["form"]["SaveImg_Config"]["AI_Det_Save"])
{
_config.saveImg.bSaveAIDetImg = value_f["form"]["SaveImg_Config"]["AI_Det_Save"].asBool();
}
} }
else else
{ {
_config.saveImg.Init(); _config.saveImg.Init();
} }
// _config.edgeDet.print("edgeDet"); // _config.pinDet.print("pinDet");
// getchar(); // getchar();
} }
if ("Big_NG" == strCode) if ("bMerge_QueXi" == strCode)
{ {
auto value_f = value; auto value_f = value;
// std::cout << value_f << std::endl; // std::cout << value_f << std::endl;
// getchar(); // getchar();
_config.bigNG.bOpen = value_f["isOpen"].asBool(); _config.Merge_QueXi.bOpen = value_f["isOpen"].asBool();
if (_config.bigNG.bOpen) }
{
if (value_f["form"]["aotudian"]["open"])
{
_config.bigNG.qx[CONFIG_QX_NAME_cell_aotudian].bOpen = value_f["form"]["aotudian"]["open"].asBool();
}
if (value_f["form"]["aotudian"]["Area"])
{
_config.bigNG.qx[CONFIG_QX_NAME_cell_aotudian].fArea = value_f["form"]["aotudian"]["Area"].asFloat();
}
_config.bigNG.qx[CONFIG_QX_NAME_cell_aotudian].strname = "aotudian";
if (value_f["form"]["other"]["open"]) // 图像模糊度(清晰度)检测: AI 检测之前执行, 模糊图片可判 NG
if ("Blur_Detect" == strCode)
{
auto value_f = value;
// std::cout << value_f << std::endl;
// getchar();
_config.blurDet.bOpen = value_f["isOpen"].asBool();
if (_config.blurDet.bOpen)
{
if (value_f["form"]["Blur_Config"]["bNG"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_other].bOpen = value_f["form"]["other"]["open"].asBool(); _config.blurDet.bNG = value_f["form"]["Blur_Config"]["bNG"].asBool();
} }
if (value_f["form"]["other"]["Area"]) // 模糊度阈值: blur_threshold(界面) / threshold 兼容; 未配置或 <= 0 时保留默认阈值
float fThreshold = 0.0f;
if (value_f["form"]["Blur_Config"]["blur_threshold"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_other].fArea = value_f["form"]["other"]["Area"].asFloat(); fThreshold = value_f["form"]["Blur_Config"]["blur_threshold"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_other].strname = "other"; else if (value_f["form"]["Blur_Config"]["threshold"])
if (value_f["form"]["line"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_line].bOpen = value_f["form"]["line"]["open"].asBool(); fThreshold = value_f["form"]["Blur_Config"]["threshold"].asFloat();
} }
if (value_f["form"]["line"]["Area"]) if (fThreshold > 0.0f)
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_line].fArea = value_f["form"]["line"]["Area"].asFloat(); _config.blurDet.fThreshold = fThreshold;
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_line].strname = "line"; }
else
{
// 关闭: 阈值置 0, 不做模糊判定
_config.blurDet.bOpen = false;
_config.blurDet.fThreshold = 0.0f;
}
// _config.blurDet.print("blurDet");
}
if (value_f["form"]["zangwu"]["open"]) if ("Big_NG" == strCode)
{
auto value_f = value;
// std::cout << value_f << std::endl;
// getchar();
_config.bigNG.bOpen = value_f["isOpen"].asBool();
if (_config.bigNG.bOpen)
{
if (value_f["form"]["PIN_PoSun"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_zangwu].bOpen = value_f["form"]["zangwu"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_PIN_PoSun].bOpen = value_f["form"]["PIN_PoSun"]["open"].asBool();
} }
if (value_f["form"]["zangwu"]["Area"]) if (value_f["form"]["PIN_PoSun"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_zangwu].fArea = value_f["form"]["zangwu"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_PIN_PoSun].fArea = value_f["form"]["PIN_PoSun"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_zangwu].strname = "zangwu"; _config.bigNG.qx[CONFIG_QX_NAME_PIN_PoSun].strname = "PIN_PoSun";
if (value_f["form"]["edge"]["open"]) if (value_f["form"]["PIN_QueShi"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_edge].bOpen = value_f["form"]["edge"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_PIN_QueShi].bOpen = value_f["form"]["PIN_QueShi"]["open"].asBool();
} }
if (value_f["form"]["edge"]["Area"]) if (value_f["form"]["PIN_QueShi"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_edge].fArea = value_f["form"]["edge"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_PIN_QueShi].fArea = value_f["form"]["PIN_QueShi"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_edge].strname = "edge"; _config.bigNG.qx[CONFIG_QX_NAME_PIN_QueShi].strname = "PIN_QueShi";
if (value_f["form"]["ymhs"]["open"]) if (value_f["form"]["BTB_DuanLie"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_ymhs].bOpen = value_f["form"]["ymhs"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_BTB_DuanLie].bOpen = value_f["form"]["BTB_DuanLie"]["open"].asBool();
} }
if (value_f["form"]["ymhs"]["Area"]) if (value_f["form"]["BTB_DuanLie"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_ymhs].fArea = value_f["form"]["ymhs"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_BTB_DuanLie].fArea = value_f["form"]["BTB_DuanLie"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_ymhs].strname = "ymhs"; _config.bigNG.qx[CONFIG_QX_NAME_BTB_DuanLie].strname = "BTB_DuanLie";
if (value_f["form"]["dianzhuang"]["open"]) if (value_f["form"]["LianXi"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_dianzhuang].bOpen = value_f["form"]["dianzhuang"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_LianXi].bOpen = value_f["form"]["LianXi"]["open"].asBool();
} }
if (value_f["form"]["dianzhuang"]["Area"]) if (value_f["form"]["LianXi"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_dianzhuang].fArea = value_f["form"]["dianzhuang"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_LianXi].fArea = value_f["form"]["LianXi"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_dianzhuang].strname = "dianzhuang"; _config.bigNG.qx[CONFIG_QX_NAME_LianXi].strname = "LianXi";
if (value_f["form"]["posun"]["open"]) if (value_f["form"]["QueXi"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_posun].bOpen = value_f["form"]["posun"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_QueXi].bOpen = value_f["form"]["QueXi"]["open"].asBool();
} }
if (value_f["form"]["posun"]["Area"]) if (value_f["form"]["QueXi"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_posun].fArea = value_f["form"]["posun"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_QueXi].fArea = value_f["form"]["QueXi"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_posun].strname = "posun"; _config.bigNG.qx[CONFIG_QX_NAME_QueXi].strname = "QueXi";
if (value_f["form"]["xianwei"]["open"]) if (value_f["form"]["YiWu"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_xianwei].bOpen = value_f["form"]["xianwei"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_YiWu].bOpen = value_f["form"]["YiWu"]["open"].asBool();
} }
if (value_f["form"]["xianwei"]["Area"]) if (value_f["form"]["YiWu"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_xianwei].fArea = value_f["form"]["xianwei"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_YiWu].fArea = value_f["form"]["YiWu"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_xianwei].strname = "xianwei"; _config.bigNG.qx[CONFIG_QX_NAME_YiWu].strname = "YiWu";
if (value_f["form"]["shuizi"]["open"]) if (value_f["form"]["C_BianXing"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_shuizi].bOpen = value_f["form"]["shuizi"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_C_BianXing].bOpen = value_f["form"]["C_BianXing"]["open"].asBool();
} }
if (value_f["form"]["shuizi"]["Area"]) if (value_f["form"]["C_BianXing"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_shuizi].fArea = value_f["form"]["shuizi"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_C_BianXing].fArea = value_f["form"]["C_BianXing"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_shuizi].strname = "shuizi"; _config.bigNG.qx[CONFIG_QX_NAME_C_BianXing].strname = "C_BianXing";
if (value_f["form"]["danban"]["open"]) if (value_f["form"]["PIN_PianYi"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_danban].bOpen = value_f["form"]["danban"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_PIN_PianYi].bOpen = value_f["form"]["PIN_PianYi"]["open"].asBool();
} }
if (value_f["form"]["danban"]["Area"]) if (value_f["form"]["PIN_PianYi"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_danban].fArea = value_f["form"]["danban"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_PIN_PianYi].fArea = value_f["form"]["PIN_PianYi"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_danban].strname = "danban"; _config.bigNG.qx[CONFIG_QX_NAME_PIN_PianYi].strname = "PIN_PianYi";
if (value_f["form"]["fuchen"]["open"]) if (value_f["form"]["Panel"]["open"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_fuchen].bOpen = value_f["form"]["fuchen"]["open"].asBool(); _config.bigNG.qx[CONFIG_QX_NAME_Panel].bOpen = value_f["form"]["Panel"]["open"].asBool();
} }
if (value_f["form"]["fuchen"]["Area"]) if (value_f["form"]["Panel"]["Area"])
{ {
_config.bigNG.qx[CONFIG_QX_NAME_cell_fuchen].fArea = value_f["form"]["fuchen"]["Area"].asFloat(); _config.bigNG.qx[CONFIG_QX_NAME_Panel].fArea = value_f["form"]["Panel"]["Area"].asFloat();
} }
_config.bigNG.qx[CONFIG_QX_NAME_cell_fuchen].strname = "fuchen"; _config.bigNG.qx[CONFIG_QX_NAME_Panel].strname = "Panel";
} }
else else
{ {
_config.bigNG.Init(); _config.bigNG.Init();
} }
// _config.edgeDet.print("edgeDet"); // _config.pinDet.print("pinDet");
// getchar(); // getchar();
} }
return 0; return 0;

@ -185,12 +185,12 @@ std::string Extract_ALL::Extract_Product_Name(std::string strPath, int userflag)
// std::cout << strPath << std::endl; // std::cout << strPath << std::endl;
fs::path p = fs::path(strPath); fs::path p = fs::path(strPath);
fs::path lastDir = p.parent_path().filename(); fs::path lastDir = p.parent_path().parent_path().filename();
std::string name = lastDir.string(); std::string name = lastDir.string();
if (name.length()<7) if (name.empty())
{ {
lastDir = p.parent_path().parent_path().filename(); lastDir = p.parent_path().filename();
name = lastDir.string(); name = lastDir.string();
} }
@ -212,24 +212,26 @@ std::string Extract_ALL::Extract_Channel_Name(std::string strPath, int userflag)
} }
int Extract_ALL::Read_Image_List(std::string strPath, std::string strproduct) int Extract_ALL::Read_Image_List(std::string strPath, std::string strproduct)
{ {
std::string strSearchImgPath = strPath + "/*.bmp"; std::vector<std::string> vecExt = {".bmp", ".png", ".jpg", ".jpeg", ".tif", ".tiff"};
Read_Camera_Product_Image(strSearchImgPath, 0, strproduct); for (size_t i = 0; i < vecExt.size(); i++)
{
// strSearchImgPath = strPath + "/*.png"; std::string strSearchImgPath = strPath + "/*" + vecExt[i];
// Read_Camera_Product_Image(strSearchImgPath, 1); Read_Camera_Product_Image(strSearchImgPath, 0, strproduct);
}
return 0; return 0;
} }
int Extract_ALL::Read_Image_List(std::string strPath_left, std::string strPath_right, std::string strproduct) int Extract_ALL::Read_Image_List(std::string strPath_left, std::string strPath_right, std::string strproduct)
{ {
std::string strSearchImgPath = strPath_left + "/*.bmp"; std::vector<std::string> vecExt = {".bmp", ".png", ".jpg", ".jpeg", ".tif", ".tiff"};
Read_Camera_Product_Image(strSearchImgPath, 0, strproduct); for (size_t i = 0; i < vecExt.size(); i++)
strSearchImgPath = strPath_right + "/*.bmp"; {
Read_Camera_Product_Image(strSearchImgPath, 0, strproduct); std::string strSearchImgPath = strPath_left + "/*" + vecExt[i];
Read_Camera_Product_Image(strSearchImgPath, 0, strproduct);
// strSearchImgPath = strPath + "/*.png"; strSearchImgPath = strPath_right + "/*" + vecExt[i];
// Read_Camera_Product_Image(strSearchImgPath, 1); Read_Camera_Product_Image(strSearchImgPath, 0, strproduct);
}
return 0; return 0;
} }

@ -5,6 +5,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <string> #include <string>
#include <algorithm>
#include "CheckUtil.hpp" #include "CheckUtil.hpp"
std::string ExtractFileNameWithoutExtension(const std::string &strImgPath) std::string ExtractFileNameWithoutExtension(const std::string &strImgPath)
{ {
@ -423,6 +424,7 @@ int deal::preCheck()
{ {
continue; continue;
} }
pushImgNum++;
} }
SetStatus_List(ThreadIdx, Thread_Status_READY); SetStatus_List(ThreadIdx, Thread_Status_READY);
GetImgNum++; GetImgNum++;
@ -458,7 +460,7 @@ int deal::preCheck()
std::shared_ptr<CheckResult> checkResult; std::shared_ptr<CheckResult> checkResult;
int re = m_pALLImgCheckAnalysisy->GetCheckReuslt(checkResult); int re = m_pALLImgCheckAnalysisy->GetCheckReuslt(checkResult);
saveImg("/home/aidlux/BOE/CELL_AOI/testresult/", checkResult); saveImg("/home/aidlux/BOE/ZB_SOCKET/testresult/", checkResult);
getresultNum++; getresultNum++;
if (getresultNum == pushImgNum) if (getresultNum == pushImgNum)
{ {
@ -804,14 +806,14 @@ void deal::ResultThread(int id)
// 存图 // 存图
// std::string savepath = "/home/aidlux/BOE/Detsave/" + runConfig.filePath + "/"; // std::string savepath = "/home/aidlux/BOE/Detsave/" + runConfig.filePath + "/";
// saveImg(savepath, dealResult); // saveImg(savepath, dealResult);
saveImg("/home/aidlux/BOE/CELL_AOI/Detsave/", dealResult); saveImg("/home/aidlux/BOE/ZB_SOCKET/Detsave/", dealResult);
} }
{ {
// 存 检测统计 日志 // 存 检测统计 日志
mutex_DetResult_.lock(); mutex_DetResult_.lock();
m_DetResult.print(false); m_DetResult.print(false);
std::string strlog = "/home/aidlux/BOE/CELL_AOI/ResultImg/"; std::string strlog = "/home/aidlux/BOE/ZB_SOCKET/ResultImg/";
if (m_strCurDate != "") if (m_strCurDate != "")
{ {
strlog += m_strCurDate; strlog += m_strCurDate;
@ -821,6 +823,8 @@ void deal::ResultThread(int id)
{ {
strlog += "/result"; strlog += "/result";
} }
// 确保日志目录存在(否则 writeLog 报 Failed to open file)
_sysmkdirs_1(strlog.substr(0, strlog.find_last_of('/')));
writeLog(strlog, m_DetResult.strlist); writeLog(strlog, m_DetResult.strlist);
mutex_DetResult_.unlock(); mutex_DetResult_.unlock();
@ -910,6 +914,30 @@ bool deal::ReadSystemConfig(const std::string &strPath)
} }
int deal::GetDetImageInfo(std::string strProductID, std::string strSearchImg, std::vector<JC_IMAGE_INFO_> &jcImageInfoList) int deal::GetDetImageInfo(std::string strProductID, std::string strSearchImg, std::vector<JC_IMAGE_INFO_> &jcImageInfoList)
{ {
// -f 批量跑新格式: 产品目录下 input 子目录, 只取 _src 原图
// (如 1M10121554R2B3_buckle_down2_src.jpg, 从图片名解析产品ID和相机名)
if (fs::is_directory(strSearchImg + "/input"))
{
jcImageInfoList.erase(jcImageInfoList.begin(), jcImageInfoList.end());
std::vector<cv::String> srcImgPaths;
cv::glob(strSearchImg + "/input/*.jpg", srcImgPaths, false);
for (int idx = 0; idx < srcImgPaths.size(); idx++)
{
JC_IMAGE_INFO_ tem;
int re = GetImgInfo_SOCKET_SRC(srcImgPaths.at(idx), &tem);
if (re != 0)
{
continue;
}
if (tem.strProductID == "")
{
tem.strProductID = strProductID;
}
jcImageInfoList.push_back(tem);
}
return 0;
}
// 1、获取图片路径 // 1、获取图片路径
std::vector<cv::String> img_paths; std::vector<cv::String> img_paths;
std::string strs1 = strSearchImg + "/*.jpg"; std::string strs1 = strSearchImg + "/*.jpg";
@ -1007,6 +1035,56 @@ int deal::GetImgInfo_POL_ET_PNG(std::string strImgPath, JC_IMAGE_INFO_ *pImageIn
} }
return 0; return 0;
} }
// -f 批量跑新格式: 解析图片名组装产品信息
// 图片名示例: 1M10121554R2B3_buckle_down2_src.jpg
// 产品ID: 第一个 '_' 之前的部分 (1M10121554R2B3)
// 相机名/通道名: 只处理文件名包含 buckle 且后缀为 _src 的原图
int deal::GetImgInfo_SOCKET_SRC(std::string strImgPath, JC_IMAGE_INFO_ *pImageInfo)
{
if (pImageInfo == NULL)
{
return 1;
}
std::string strName = ExtractFileNameWithoutExtension(strImgPath);
// 只处理文件名包含 buckle 的图片, 跳过 t_face_bright/t_face_dark 等其它相机
if (strName.find("buckle") == std::string::npos)
{
printf("GetImgInfo_SOCKET_SRC: no buckle in name, skip %s\n", strImgPath.c_str());
return 1;
}
// 只处理后缀为 _src 的原图, 跳过 _roi 等其它图
const std::string strSrcSuffix = "_src";
if (strName.size() <= strSrcSuffix.size() ||
strName.compare(strName.size() - strSrcSuffix.size(), strSrcSuffix.size(), strSrcSuffix) != 0)
{
return 1;
}
// 相机名同时作为通道名
std::string strCamName = "buckle";
// 产品ID = 文件名第一个 '_' 之前的部分
std::string strProductID = strName;
size_t nPos = strName.find('_');
if (nPos != std::string::npos)
{
strProductID = strName.substr(0, nPos);
}
pImageInfo->strProductID = strProductID;
pImageInfo->strCamID = strCamName;
pImageInfo->strchannelName = strCamName;
pImageInfo->strName = strName;
pImageInfo->strPath = strImgPath;
printf("strImgPath %s strName %s strProductID %s strCam %s\n",
strImgPath.c_str(), strName.c_str(), strProductID.c_str(), strCamName.c_str());
return 0;
}
int deal::InsertDetResult(ReadImgInfo tem) int deal::InsertDetResult(ReadImgInfo tem)
{ {
std::lock_guard<std::mutex> lock(mtx_List[Mutex_Type_DetResult]); std::lock_guard<std::mutex> lock(mtx_List[Mutex_Type_DetResult]);
@ -1167,7 +1245,12 @@ int deal::DetImg()
{ {
m_nReadThread_type = READ_THREAD_TYPE_READIMG; m_nReadThread_type = READ_THREAD_TYPE_READIMG;
StartThread(THREAD_RUN_Only_ReadImg); StartThread(THREAD_RUN_Only_ReadImg);
preCheck(); // -8 循环执行:像 -f 批量一样,线程只启动一次,内部重复跑同一套图
do
{
preCheck();
usleep(10 * 1000);
} while (runConfig.bLoop);
return 0; return 0;
} }
@ -1198,6 +1281,36 @@ int deal::DetImg()
// getchar(); // getchar();
m_nSystemCheckType = CHECK_TYPE_OFFLING; m_nSystemCheckType = CHECK_TYPE_OFFLING;
StartThread(THREAD_RUN_ALL); StartThread(THREAD_RUN_ALL);
// -8 循环执行:保持常驻,重复处理同一批产品
if (runConfig.bLoop)
{
while (true)
{
usleep(1000 * 1000);
}
}
// -f 批量(不加 -8 只执行一次):等待全部产品处理完成后退出
// DealImg 线程处理完一批后会清空 m_product_ID_List 并等待结果队列清空
while (true)
{
usleep(200 * 1000);
size_t nResultSize = 0;
{
std::lock_guard<std::mutex> lock(mutex_Result_list);
nResultSize = m_Result_list.size();
}
if (m_product_ID_List.empty() && nResultSize <= 0)
{
break;
}
}
// 留时间给结果线程把最后一组结果写完
usleep(2 * 1000 * 1000);
printf(">>> -f batch complete, exit \n");
fflush(NULL);
_exit(0);
return 0; return 0;
} }
int deal::ReJson_product(std::vector<Json_Det_Path> &jsonList, std::string strProductName) int deal::ReJson_product(std::vector<Json_Det_Path> &jsonList, std::string strProductName)
@ -1224,7 +1337,7 @@ int deal::ReJson_product(std::vector<Json_Det_Path> &jsonList, std::string strPr
std::string json_str = buffer.str(); // 获取 JSON 文件的内容作为字符串 std::string json_str = buffer.str(); // 获取 JSON 文件的内容作为字符串
// 显示读取到的 JSON 字符串 // 显示读取到的 JSON 字符串
// std::cout << "JSON content: " << json_str << std::endl; // std::cout << "JSON content: " << json_str << std::endl;
cv::Mat cutimg = cv::imread(strimg, 0); cv::Mat cutimg = cv::imread(strimg, cv::IMREAD_UNCHANGED);
int f0 = strJson.rfind('_'); int f0 = strJson.rfind('_');
int f1 = strJson.rfind('.'); int f1 = strJson.rfind('.');
int f2 = strJson.rfind('/'); int f2 = strJson.rfind('/');
@ -1459,6 +1572,8 @@ int deal::InitCPUIDX()
start += READ_IMG_THREAD_NUM; start += READ_IMG_THREAD_NUM;
m_CPUInfo[THREAD_CPU_Main_saveImg].set(start, Save_IMG_THREAD_NUM); m_CPUInfo[THREAD_CPU_Main_saveImg].set(start, Save_IMG_THREAD_NUM);
start += Save_IMG_THREAD_NUM; start += Save_IMG_THREAD_NUM;
// 注意:检测线程的核带已下沉到库内部(AlgorithmModule/include/Define_Base.h 的
// CHECK_CPU_BAND_START / CHECK_CPU_BAND_NUM = 24 / 8),下面这项仅为兼容赋值,不再影响检测线程绑核。
m_CPUInfo[THREAD_CPU_CheckSo].set(start, 15); m_CPUInfo[THREAD_CPU_CheckSo].set(start, 15);
return 0; return 0;
} }
@ -1478,12 +1593,12 @@ void deal::Thread_ReadImg(int id)
printf("pImageInfo == nullptr=================id %d=============\n", id); printf("pImageInfo == nullptr=================id %d=============\n", id);
return; return;
} }
// pImageInfo->img = cv::imread(pImageInfo->strPath, 0); // 原图保持原始通道(彩色图不再强制转灰度),结果图/缺陷小图才能显示真实色彩
pImageInfo->img = cv::imread(pImageInfo->strPath, 0); pImageInfo->img = cv::imread(pImageInfo->strPath, cv::IMREAD_UNCHANGED);
// cv::imwrite(pImageInfo->strchannelName + "_A_ssssde.png", pImageInfo->img); // cv::imwrite(pImageInfo->strchannelName + "_A_ssssde.png", pImageInfo->img);
if (pImageInfo->strPath_B != "") if (pImageInfo->strPath_B != "")
{ {
pImageInfo->img_B = cv::imread(pImageInfo->strPath_B, 0); pImageInfo->img_B = cv::imread(pImageInfo->strPath_B, cv::IMREAD_UNCHANGED);
// cv::imwrite(pImageInfo->strchannelName + "_B_sssde.png", pImageInfo->img_B); // cv::imwrite(pImageInfo->strchannelName + "_B_sssde.png", pImageInfo->img_B);
} }
@ -1545,6 +1660,55 @@ int deal::LoadProductID(std::string strImgPath)
m_product_ID_List.erase(m_product_ID_List.begin(), m_product_ID_List.end()); m_product_ID_List.erase(m_product_ID_List.begin(), m_product_ID_List.end());
std::vector<cv::String> img_paths; std::vector<cv::String> img_paths;
// -f 批量跑新格式适配(参考 data/img/test-2 结构):
// <filePath>/<产品ID>_<时间戳>/input/<产品ID>_<相机名>_<位置>_src.jpg
// 也支持直接传入单个产品目录或产品目录下的 input 目录
{
std::string strPath = strImgPath;
while (!strPath.empty() && strPath[strPath.size() - 1] == '/')
{
strPath.erase(strPath.size() - 1);
}
fs::path rootPath(strPath);
if (!strPath.empty() && fs::is_directory(rootPath))
{
// 兼容直接传入 input 目录: 使用其上一级产品目录
if (rootPath.filename() == "input" && rootPath.has_parent_path())
{
rootPath = rootPath.parent_path();
}
std::vector<std::string> productDirList;
if (fs::is_directory(rootPath / "input"))
{
// 直接传入单个产品目录
productDirList.push_back(rootPath.string());
}
else
{
// 批量根目录: 含 input 子目录的子目录视为一个产品
for (const auto &entry : fs::directory_iterator(rootPath))
{
if (entry.is_directory() && fs::is_directory(entry.path() / "input"))
{
productDirList.push_back(entry.path().string());
}
}
}
if (!productDirList.empty())
{
std::sort(productDirList.begin(), productDirList.end());
for (const auto &strProductDir : productDirList)
{
m_product_ID_List.push_back(strProductDir);
std::cout << "new format product dir: " << strProductDir << std::endl;
}
return 0;
}
}
}
// tif 格式 // tif 格式
{ {
std::string str_bmp = strImgPath + "/*.jpg"; std::string str_bmp = strImgPath + "/*.jpg";
@ -1593,10 +1757,20 @@ int deal::LoadProductID(std::string strImgPath)
} }
} }
if (m_product_ID_List.empty())
{
printf("warning: no product dir/image found under %s\n", strImgPath.c_str());
}
return 0; return 0;
} }
int deal::DelImg_Cell_ET() int deal::DelImg_Cell_ET()
{ {
// -8 循环执行:每次重新加载产品列表,重复跑同一批产品
if (runConfig.bLoop && runConfig.filePath != "")
{
LoadProductID(runConfig.filePath);
}
size_t totalSize = m_product_ID_List.size(); size_t totalSize = m_product_ID_List.size();
if (totalSize > 0) if (totalSize > 0)
@ -1638,6 +1812,13 @@ int deal::Det_OneProduct_Cell_ET(std::string strProductName, int Idx)
{ {
std::string strProductID = GetFileName(strProductName); std::string strProductID = GetFileName(strProductName);
// -f 批量跑新格式: 产品目录名为 <产品ID>_<时间戳>(如 1M10121554R2B3_20261012.151453.582176)
// 取第一个 '_' 之前的部分作为产品ID; 旧格式目录名本身即产品ID(无 '_')不受影响
size_t nProductIDPos = strProductID.find('_');
if (nProductIDPos != std::string::npos)
{
strProductID = strProductID.substr(0, nProductIDPos);
}
// 获取图片路径 // 获取图片路径
std::vector<JC_IMAGE_INFO_> detImgInfoList; std::vector<JC_IMAGE_INFO_> detImgInfoList;

@ -499,6 +499,7 @@ struct DealRunConfig
RUNTYPE_ det_Type = RUNTYPE_RUN_Pre; // 处理 RUNTYPE_ det_Type = RUNTYPE_RUN_Pre; // 处理
std::string filePath = ""; // 处理文件路径 如果为空 处理当前的单张图片 std::string filePath = ""; // 处理文件路径 如果为空 处理当前的单张图片
bool bSaveProcessImg = false; // 是否中间图片图片 bool bSaveProcessImg = false; // 是否中间图片图片
bool bLoop = false; // -8 循环执行(常驻)
void print(std::string str) void print(std::string str)
{ {
printf(">>>>>>>>>>>>>>> %s <<<<<<<<<<<<\n", str.c_str()); printf(">>>>>>>>>>>>>>> %s <<<<<<<<<<<<\n", str.c_str());
@ -550,6 +551,9 @@ private:
int GetImgInfo_POL_ET_PNG(std::string strImgPath, JC_IMAGE_INFO_ *pImageInfo); int GetImgInfo_POL_ET_PNG(std::string strImgPath, JC_IMAGE_INFO_ *pImageInfo);
// -f 批量跑新格式: 解析 <产品ID>_<相机名>_<位置>_src.jpg 图片名组装产品信息(相机名同时作为通道名)
int GetImgInfo_SOCKET_SRC(std::string strImgPath, JC_IMAGE_INFO_ *pImageInfo);
// 功能测试 // 功能测试
int Det_Funtion_Test(); int Det_Funtion_Test();
// 边缘测试 // 边缘测试

@ -85,6 +85,8 @@ int main(int argc, char *argv[])
cout << "*** 5、./test_JBL_Check -falign filepath num : 批量测试定位算法,num 至多处理张数,结果:/home/aidlux/BOE/Align" << endl; cout << "*** 5、./test_JBL_Check -falign filepath num : 批量测试定位算法,num 至多处理张数,结果:/home/aidlux/BOE/Align" << endl;
cout << "*** 6、./test_JBL_Check -rjson filepath: 单张复测,filepath xx/xx/3A3K380001B1DK_20240408_152157_Main_0_2_L255" << endl; cout << "*** 6、./test_JBL_Check -rjson filepath: 单张复测,filepath xx/xx/3A3K380001B1DK_20240408_152157_Main_0_2_L255" << endl;
cout << "*** 6、./test_JBL_Check -rjsonall filepath: 一套图复测,filepath xx/xx/" << endl; cout << "*** 6、./test_JBL_Check -rjsonall filepath: 一套图复测,filepath xx/xx/" << endl;
cout << "*** 7、./test_SOCKET -8: 循环执行程序(常驻),不加 -8 只执行一次" << endl;
cout << "*** 8、./test_SOCKET -f filepath: 批量执行 filepath 下的套图(新格式: <产品ID>_<时间戳>/input/<产品ID>_<相机名>_xxx_src.jpg)" << endl;
cout << "******************************************************" << endl; cout << "******************************************************" << endl;
return 0; return 0;
@ -145,12 +147,21 @@ int main(int argc, char *argv[])
test.runConfig.print("config"); test.runConfig.print("config");
// getchar(); // getchar();
signal(SIGINT, handler); signal(SIGINT, handler);
test.start();
while (true) // -8: 循环执行程序(常驻);默认:执行一次后退出
bool bLoop = false;
for (int i = 1; i < argc; i++)
{ {
usleep(10 * 1000); if (string(argv[i]) == "-8")
{
bLoop = true;
break;
}
} }
test.runConfig.bLoop = bLoop;
test.start();
return 0; return 0;
} }

Loading…
Cancel
Save