Compare commits

...

17 Commits

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

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

@ -153,6 +153,8 @@ public:
void AddLog(std::string str);
// 添加图片信息
int AddDetImage(std::shared_ptr<shareImage> p);
// ✅ 处理完成后释放中间数据,减少内存占用
void ReleaseIntermediateData();
DetStep getStep();
void setStep(DetStep step);
bool IsNotDet();

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

@ -724,6 +724,10 @@ int CameraCheckAnalysisy::preDet_BQ(const cv::Mat &L255CutImg, std::shared_ptr<I
for (int i = 0; i < m_pCheck_Result->cameraBaseResult->pBQ_Result->pBQ_roiList.size(); i++)
{
cv::Rect boundingRect = m_pCheck_Result->cameraBaseResult->pBQ_Result->pBQ_roiList.at(i);
boundingRect.x -= 5;
boundingRect.y -= 5;
boundingRect.width += 10;
boundingRect.height += 10;
detImg_mask(boundingRect).setTo(255);
}
if (L255->result->in_shareImage->bDebugsaveImg)

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

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

@ -415,6 +415,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
Judge_Status = true;
checkFlage = ANALYSIS_TYPE_TF;
nerrortype = 1;
bNG_Status = true;
}
}
// 暗点 3S 分析 对 好品 或者 是 疑是的缺陷进行3S分析。S数量分析。 3S的暗点直接NG.
@ -626,7 +627,9 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
m_CheckResult_shareP->nresult = nqx_type;
}
#ifdef AI_Time
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
#endif
m_CheckResult_shareP->qxImageResult.push_back(tem);
}
else
@ -637,7 +640,9 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
m_CheckResult_shareP->nYS_result = nqx_type;
}
#ifdef AI_Time
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
#endif
m_CheckResult_shareP->YS_ImageResult.push_back(tem);
}
@ -885,7 +890,9 @@ int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult,
{
m_CheckResult_shareP->nresult = nqx_type;
}
#ifdef AI_Time
GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img);
#endif
m_CheckResult_shareP->qxImageResult.push_back(tem);
}
}

@ -170,7 +170,7 @@ int ImgCheckAnalysisy::GetStatus()
std::string ImgCheckAnalysisy::GetVersion()
{
return std::string("BOE_1.2.0");
return std::string("BOE_1.2.3_" + std::string(__DATE__) + "_" + std::string(__TIME__));
}
std::string ImgCheckAnalysisy::GetErrorInfo()
@ -332,7 +332,7 @@ cv::Scalar ImgCheckAnalysisy::calc_blob_info_withstats(cv::Mat &img, const cv::M
// 计算 hj差异图像大于0的像素均值
// double hj = std::abs(fbk - fdet);
double hj = CheckUtil::CalHj(cimg, cmask, mean_bk.val[0]);
double hj = CheckUtil::CalHjWeighted(cimg, cmask, fbk, 2.0f);
int worb = 0;
if (fdet >= fbk)
@ -381,7 +381,7 @@ double ImgCheckAnalysisy::CalBlobHJ(cv::Mat &img, const cv::Mat &mask, cv::Rect
// 计算 hj差异图像大于0的像素均值
// double hj = std::abs(fbk - fdet);
double hj = CheckUtil::CalHj(cimg, cmask, fbk);
double hj = CheckUtil::CalHjWeighted(cimg, cmask, fbk, 2.0f);
static int kkk = 0;
unsigned char *pErrordata = (unsigned char *)cimg.data;
@ -589,6 +589,7 @@ int ImgCheckAnalysisy::SetNewConfig()
m_pRegionAnalysisyParam = &m_pCommonAnalysisyConfig->regionConfigArr.at(0);
GetParamidx();
UpdateImgageScale();
UPdateLDConfig();
if (true)
{
printf("SetNewConfig m_nConfigIdx %d m_CheckConfig.strSkuName %s \n", m_nConfigIdx, m_AnalysisyConfig.strSkuName.c_str());
@ -666,6 +667,8 @@ int ImgCheckAnalysisy::Run(int nId)
CheckRun();
m_nRun_Status = CHECK_THREAD_STATUS_COMPLETE;
m_pImageAllResult->setStep(ImageAllResult::DetStep_Complet);
// ✅ 处理完成后立即释放 ImageAllResult 的中间数据,减少内存占用
m_pImageAllResult->ReleaseIntermediateData();
m_nRun_Status = CHECK_THREAD_STATUS_IDLE;
// printf("*--------%d\n", m_nRun_Status);
}
@ -2482,6 +2485,11 @@ int ImgCheckAnalysisy::AI_Detect_QX()
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, strBaseLog, "=======model use Chess");
pAI_Model = AI_Factory->AI_defect_Chess;
}
else if (m_pFuntion->function.f_BaseDet.strAIMode == "RGB-HGRAY")
{
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, strBaseLog, "=======model use RE_RGBHGRAY");
pAI_Model = AI_Factory->AI_defect_RE_RGBHGRAY;
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, strBaseLog, "=======model use base");
@ -3356,17 +3364,17 @@ int ImgCheckAnalysisy::BLobToDetResult()
config_qx_type == CONFIG_QX_NAME_Other ||
config_qx_type == CONFIG_QX_NAME_LD)
{
std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type];
qx_name = CONFIG_QX_NAME_Names[config_qx_type];
// m_TemCheck.AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "LD", "stsrt config_qx_type %s ", qx_name.c_str());
int dbresult = LDJudge(config_qx_type, roi, JudgArea, blobs.blobTab[i].maxValue, blobs.blobTab[i].grayDis, pQxlog);
int detre = 1;
//int detre = 1;
// 表示L0 和 DP 都有的 亮的
if (dbresult == 1)
{
config_qx_type = CONFIG_QX_NAME_LD;
qx_name = CONFIG_QX_NAME_Names[config_qx_type];
}
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, " LD Judge ", "%s qx %s ", Re_TO_STR_False(detre), qx_name.c_str());
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, " LD Judge ", "%s qx %s ", Re_TO_STR_Pass_1(dbresult), qx_name.c_str());
}
// 如果是chess 画面缺陷类型直接是chess异常。
if (m_pFuntion->function.f_AIQX.bAllToChess)
@ -3450,7 +3458,7 @@ int ImgCheckAnalysisy::BLobToDetResult()
bool ban = JudgeQXAnalysis(config_qx_type, pQxlog);
if (!ban)
{
std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type];
qx_name = CONFIG_QX_NAME_Names[config_qx_type];
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "Judge QX", "qx function close, config_qx_type %s Not Det",
qx_name.c_str());
@ -3467,7 +3475,7 @@ int ImgCheckAnalysisy::BLobToDetResult()
isMarksheildQX = Judge_MarkLine_QX(config_qx_type, roi, pQxlog);
if (isMarksheildQX)
{
std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type];
qx_name = CONFIG_QX_NAME_Names[config_qx_type];
pQxlog->AddCheckstr(PrintLevel_2, DET_LOG_LEVEL_3, "MarkLine_QX", "MarkLine_QX close, config_qx_type %s Not Det",
qx_name.c_str());

@ -972,6 +972,7 @@ int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx)
}
A_2S_num += AD_list.at(ad_i).num_2s;
A_3S_num += AD_list.at(ad_i).num_3s;
if(AD_list.at(ad_i).num_1s >= 3) A_3S_num++;
m_pMergedetlog->AddCheckstr(PrintLevel_2, 3, "AD RGBL255", "AD_list %d %s",
ad_i, AD_list.at(ad_i).GetInfo().c_str());
}
@ -1028,7 +1029,7 @@ int QX_Merge_Analysis::Analysis_AD(ALL_Qx_DataList *pALLTypeqxList, int qx_idx)
}
else
{
m_pMergedetlog->AddCheckstr(PrintLevel_2, 3, "AD RGBL255", "3S Analysis --> %s result NG 3S %d < 1",
m_pMergedetlog->AddCheckstr(PrintLevel_2, 3, "AD RGBL255", "3S Analysis --> %s result OK 3S %d < 1",
pQXChannelList->channel_name.c_str(), A_3S_num);
}
}

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

@ -203,6 +203,7 @@ int ConfigManager::UpdateConfig()
if (!std::filesystem::exists(strConfigPath)) {
strConfigPath = m_strConfigRootPath + "/param.json";
}
printf("read param %s\n", strConfigPath.c_str());
std::shared_ptr<ConfigBase> temConfig = ConfigBase::GetInstance();
re = LoadParamConfig(temConfig, strConfigPath);
if (re != 0)
@ -218,7 +219,12 @@ int ConfigManager::UpdateConfig()
printf("Error >>>> camear Name is empty \n");
continue;
}
Config_instances_[p.commonCheckConfig.baseConfig.strCamearName] = temConfig;
auto it = Config_instances_.find(p.commonCheckConfig.baseConfig.strCamearName);
if (it != Config_instances_.end() && it->second) {
LoadParamConfig(it->second, strConfigPath);
} else {
Config_instances_[p.commonCheckConfig.baseConfig.strCamearName] = temConfig;
}
}
// getchar();

@ -32,7 +32,7 @@ void CommonParamToCheckConfigJson::toObjectFromValue(Json::Value root)
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
Json::Value rootvalue;
std::string err;
// std::cout << strJson << std::endl;
std::cout << strJson << std::endl;
auto nSize = strJson.size();
if (reader->parse(strJson.c_str(), strJson.c_str() + nSize, &rootvalue, &err))
{
@ -48,7 +48,8 @@ void CommonParamToCheckConfigJson::toObjectFromValue(Json::Value root)
if ( _config.baseConfig.strConfigVersion == "")
{
_config.baseConfig.strConfigVersion = "NULL";
}
}
_config.baseConfig.image_widht = value["image_widht"].asInt();
_config.baseConfig.Image_height = value["Image_height"].asInt();
_config.baseConfig.bDrawShieldRoi = value["bDrawShieldRoi"].asBool();

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

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

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

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

Loading…
Cancel
Save