|
|
|
|
@ -1327,8 +1327,9 @@ int deal::DetImg()
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// @brief 监控模式:每 5 分钟执行一次模型推理,并把当前设备资源消耗(CPU 内存 / GPU 显存)
|
|
|
|
|
/// 写入 monitor.log;当某次推理输出 out 图至少一个像素不为全黑时,保存该 out 图并退出监控。
|
|
|
|
|
/// @brief 监控模式:每 10 秒执行一次模型推理,并把当前设备资源消耗(CPU 内存 / GPU 显存)
|
|
|
|
|
/// 写入 monitor.log;out 图重复覆盖保存到输入图同名 out 文件(*_in.png -> *_out.png),
|
|
|
|
|
/// 当某次推理输出 out 图至少一个像素不为全黑时退出监控。
|
|
|
|
|
/// @return 0 表示成功,非 0 表示初始化失败
|
|
|
|
|
int deal::MonitorRun()
|
|
|
|
|
{
|
|
|
|
|
@ -1397,9 +1398,25 @@ int deal::MonitorRun()
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 预处理成模型输入(只读一次,每轮复用)
|
|
|
|
|
// 输出目录:输入目录去掉末尾 '/' 后追加 "_TEST/"(与 TestAIDet 保持一致)
|
|
|
|
|
std::string strDstRoot = strSrcRoot;
|
|
|
|
|
while (strDstRoot.size() > 1 && strDstRoot.back() == '/')
|
|
|
|
|
{
|
|
|
|
|
strDstRoot.pop_back();
|
|
|
|
|
}
|
|
|
|
|
strDstRoot += "_TEST/";
|
|
|
|
|
{
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::create_directories(strDstRoot, ec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 预处理成模型输入(只读一次,每轮复用),并计算对应输出路径(*_in.png -> *_out.png)
|
|
|
|
|
std::vector<std::string> okInList;
|
|
|
|
|
std::vector<cv::Mat> inImgs;
|
|
|
|
|
std::vector<std::string> outList;
|
|
|
|
|
okInList.reserve(inList.size());
|
|
|
|
|
inImgs.reserve(inList.size());
|
|
|
|
|
outList.reserve(inList.size());
|
|
|
|
|
for (size_t i = 0; i < inList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
cv::Mat inImg = cv::imread(inList[i], cv::IMREAD_UNCHANGED);
|
|
|
|
|
@ -1429,7 +1446,18 @@ int deal::MonitorRun()
|
|
|
|
|
{
|
|
|
|
|
cv::resize(inImg, inImg, cv::Size(nModelInW, nModelInH));
|
|
|
|
|
}
|
|
|
|
|
okInList.push_back(inList[i]);
|
|
|
|
|
inImgs.push_back(inImg);
|
|
|
|
|
|
|
|
|
|
std::string rel = inList[i].substr(strSrcRoot.size());
|
|
|
|
|
const std::string inSuffix = "_in.png";
|
|
|
|
|
const std::string outSuffix = "_out.png";
|
|
|
|
|
size_t pos = rel.rfind(inSuffix);
|
|
|
|
|
if (pos != std::string::npos)
|
|
|
|
|
{
|
|
|
|
|
rel.replace(pos, inSuffix.size(), outSuffix);
|
|
|
|
|
}
|
|
|
|
|
outList.push_back(strDstRoot + rel);
|
|
|
|
|
}
|
|
|
|
|
if (inImgs.empty())
|
|
|
|
|
{
|
|
|
|
|
@ -1437,28 +1465,8 @@ int deal::MonitorRun()
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 输出目录:输入目录去掉末尾 '/' 后追加 "_TEST/"(与 TestAIDet 保持一致)
|
|
|
|
|
std::string strDstRoot = strSrcRoot;
|
|
|
|
|
while (strDstRoot.size() > 1 && strDstRoot.back() == '/')
|
|
|
|
|
{
|
|
|
|
|
strDstRoot.pop_back();
|
|
|
|
|
}
|
|
|
|
|
strDstRoot += "_TEST/";
|
|
|
|
|
|
|
|
|
|
// 本次监控的时间戳输出目录(存图和 log 都放这里)
|
|
|
|
|
time_t startT = time(NULL);
|
|
|
|
|
struct tm stmv;
|
|
|
|
|
localtime_r(&startT, &stmv);
|
|
|
|
|
char dirTs[64] = {0};
|
|
|
|
|
strftime(dirTs, sizeof(dirTs), "monitor_%Y%m%d_%H%M%S", &stmv);
|
|
|
|
|
std::string strOutDir = strDstRoot + dirTs + "/";
|
|
|
|
|
{
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::create_directories(strOutDir, ec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 打开监控日志(追加写)
|
|
|
|
|
std::string strLogPath = strOutDir + "monitor.log";
|
|
|
|
|
std::string strLogPath = strDstRoot + "monitor.log";
|
|
|
|
|
std::ofstream logFile(strLogPath, std::ios::app);
|
|
|
|
|
if (!logFile)
|
|
|
|
|
{
|
|
|
|
|
@ -1474,11 +1482,9 @@ int deal::MonitorRun()
|
|
|
|
|
logFile.flush();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const int nIntervalSec = 5 * 60; // 5 分钟
|
|
|
|
|
const int nMaxRunSec = 30 * 60; // 30 分钟
|
|
|
|
|
const int nIntervalSec = 1; // 1 秒
|
|
|
|
|
long nRound = 0;
|
|
|
|
|
auto monitorStart = std::chrono::steady_clock::now();
|
|
|
|
|
logLine("===== monitor start (run max 30 min) =====");
|
|
|
|
|
logLine("===== monitor start (every 10s, exit on first non-black out) =====");
|
|
|
|
|
|
|
|
|
|
// 初始化 CPU 采样基准(用于计算 CPU 使用率)
|
|
|
|
|
struct rusage usagePrev;
|
|
|
|
|
@ -1489,16 +1495,6 @@ int deal::MonitorRun()
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
// 0. 运行 30 分钟后停止
|
|
|
|
|
double elapsedSec = std::chrono::duration<double>(
|
|
|
|
|
std::chrono::steady_clock::now() - monitorStart)
|
|
|
|
|
.count();
|
|
|
|
|
if (elapsedSec >= nMaxRunSec)
|
|
|
|
|
{
|
|
|
|
|
logLine("===== monitor exit (timeout 30 min) =====");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nRound++;
|
|
|
|
|
|
|
|
|
|
// 1. 采集设备资源消耗
|
|
|
|
|
@ -1557,8 +1553,8 @@ int deal::MonitorRun()
|
|
|
|
|
gpuUsedMB, gpuTotalMB);
|
|
|
|
|
logLine(buf);
|
|
|
|
|
|
|
|
|
|
// 2. 逐图推理,遇到 out 非全黑则保存对应的输入图和输出图
|
|
|
|
|
int nHit = 0;
|
|
|
|
|
// 2. 逐图推理,out 图重复覆盖保存;遇到非全黑则退出
|
|
|
|
|
bool bHit = false;
|
|
|
|
|
for (size_t i = 0; i < inImgs.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
cv::Mat outImg;
|
|
|
|
|
@ -1567,49 +1563,215 @@ int deal::MonitorRun()
|
|
|
|
|
{
|
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
|
"[round %ld] infer fail(%d) on image %zu: %s",
|
|
|
|
|
nRound, re, i, inList[i].c_str());
|
|
|
|
|
nRound, re, i, okInList[i].c_str());
|
|
|
|
|
logLine(buf);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 输出图重复覆盖保存到 *_out.png
|
|
|
|
|
bool bSaved = false;
|
|
|
|
|
{
|
|
|
|
|
fs::path outParent = fs::path(outList[i]).parent_path();
|
|
|
|
|
if (!outParent.empty())
|
|
|
|
|
{
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::create_directories(outParent, ec);
|
|
|
|
|
}
|
|
|
|
|
bSaved = cv::imwrite(outList[i], outImg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int nNonZero = cv::countNonZero(outImg);
|
|
|
|
|
if (nNonZero > 0)
|
|
|
|
|
{
|
|
|
|
|
// 输出图不全黑 -> 保存对应的输入图和输出图
|
|
|
|
|
std::string saveInPath = strOutDir + "monitor_hit_round" +
|
|
|
|
|
std::to_string(nRound) + "_img" +
|
|
|
|
|
std::to_string(i) + "_in.png";
|
|
|
|
|
std::string saveOutPath = strOutDir + "monitor_hit_round" +
|
|
|
|
|
std::to_string(nRound) + "_img" +
|
|
|
|
|
std::to_string(i) + "_out.png";
|
|
|
|
|
cv::imwrite(saveInPath, inImgs[i]);
|
|
|
|
|
cv::imwrite(saveOutPath, outImg);
|
|
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf),
|
|
|
|
|
"[round %ld] hit! out image %zu non-zero pixels: %d -> save %s / %s",
|
|
|
|
|
nRound, i, nNonZero, saveInPath.c_str(), saveOutPath.c_str());
|
|
|
|
|
"[round %ld] hit! out image %zu non-zero pixels: %d -> %s (saved=%d), exit monitor",
|
|
|
|
|
nRound, i, nNonZero, outList[i].c_str(), bSaved ? 1 : 0);
|
|
|
|
|
logLine(buf);
|
|
|
|
|
nHit++;
|
|
|
|
|
bHit = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "[round %ld] %d non-black out saved, sleep %d s ...",
|
|
|
|
|
nRound, nHit, nIntervalSec);
|
|
|
|
|
logLine(buf);
|
|
|
|
|
|
|
|
|
|
// 3. 休眠(不超过剩余运行时间)
|
|
|
|
|
double remainSec = nMaxRunSec - elapsedSec;
|
|
|
|
|
int sleepSec = (int)(remainSec < nIntervalSec ? remainSec : nIntervalSec);
|
|
|
|
|
if (sleepSec > 0)
|
|
|
|
|
if (bHit)
|
|
|
|
|
{
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(sleepSec));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "[round %ld] all out black, sleep %d s ...", nRound, nIntervalSec);
|
|
|
|
|
logLine(buf);
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(nIntervalSec));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logFile.close();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// @brief AI 检测独立测试:遍历 /home/aidlux/BOE/FOG/AI_Detect/ 下所有 *_in.png,
|
|
|
|
|
/// 送入 AI_defect_NF 模型推理,将输出 *_out.png 保存到 /home/aidlux/BOE/FOG/AI_Detect_TEST/
|
|
|
|
|
/// @return 0 表示成功
|
|
|
|
|
/*int deal::TestAIDet()
|
|
|
|
|
{
|
|
|
|
|
// 输入目录:优先使用命令行指定路径,否则使用默认路径
|
|
|
|
|
std::string strSrcRoot = runConfig.filePath;
|
|
|
|
|
if (strSrcRoot.empty())
|
|
|
|
|
{
|
|
|
|
|
strSrcRoot = "/home/aidlux/BOE/FOG/AI_Detect/";
|
|
|
|
|
}
|
|
|
|
|
else if (strSrcRoot.back() != '/')
|
|
|
|
|
{
|
|
|
|
|
strSrcRoot += "/";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 输出目录:输入目录去掉末尾 '/' 后追加 "_TEST/"
|
|
|
|
|
// 默认输入 /home/aidlux/BOE/FOG/AI_Detect/ -> 输出 /home/aidlux/BOE/FOG/AI_Detect_TEST/
|
|
|
|
|
std::string strDstRoot = strSrcRoot;
|
|
|
|
|
while (strDstRoot.size() > 1 && strDstRoot.back() == '/')
|
|
|
|
|
{
|
|
|
|
|
strDstRoot.pop_back();
|
|
|
|
|
}
|
|
|
|
|
strDstRoot += "_TEST/";
|
|
|
|
|
|
|
|
|
|
if(runConfig.bmonitor)
|
|
|
|
|
{
|
|
|
|
|
// 监控模式:每 5 分钟执行一次模型推理,记录设备资源消耗到 log;
|
|
|
|
|
// 当某次推理输出 out 图至少一个像素不为全黑时退出监控。
|
|
|
|
|
return MonitorRun();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<AIFactory> pAIFactory = AIFactory::GetInstance();
|
|
|
|
|
std::shared_ptr<AIModel_Base> pAI_Model = pAIFactory->AI_defect_NF;
|
|
|
|
|
if (!pAI_Model || !pAI_Model->m_bInitSuccess)
|
|
|
|
|
{
|
|
|
|
|
printf("TestAIDet: AI model (AI_defect_NF) not ready!\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("TestAIDet: model input [c=%d w=%d h=%d] output [c=%d w=%d h=%d]\n",
|
|
|
|
|
pAI_Model->input_0.channel, pAI_Model->input_0.width, pAI_Model->input_0.height,
|
|
|
|
|
pAI_Model->output_0.channel, pAI_Model->output_0.width, pAI_Model->output_0.height);
|
|
|
|
|
|
|
|
|
|
// 收集所有 *_in.png 文件
|
|
|
|
|
std::vector<std::string> inList;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (!fs::exists(strSrcRoot))
|
|
|
|
|
{
|
|
|
|
|
printf("TestAIDet: src dir not exist: %s\n", strSrcRoot.c_str());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
for (const auto &entry : fs::recursive_directory_iterator(strSrcRoot))
|
|
|
|
|
{
|
|
|
|
|
if (!entry.is_regular_file())
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
std::string path = entry.path().string();
|
|
|
|
|
std::string name = entry.path().filename().string();
|
|
|
|
|
const std::string suffix = "_in.png";
|
|
|
|
|
if (name.size() >= suffix.size() &&
|
|
|
|
|
name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0)
|
|
|
|
|
{
|
|
|
|
|
inList.push_back(path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception &e)
|
|
|
|
|
{
|
|
|
|
|
printf("TestAIDet: traverse error %s\n", e.what());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::sort(inList.begin(), inList.end());
|
|
|
|
|
printf("TestAIDet: total %zu in images\n", inList.size());
|
|
|
|
|
|
|
|
|
|
const int nModelInCh = pAI_Model->input_0.channel;
|
|
|
|
|
const int nModelInW = pAI_Model->input_0.width;
|
|
|
|
|
const int nModelInH = pAI_Model->input_0.height;
|
|
|
|
|
|
|
|
|
|
int nOK = 0;
|
|
|
|
|
for (size_t i = 0; i < inList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
const std::string &inPath = inList[i];
|
|
|
|
|
cv::Mat inImg = cv::imread(inPath, cv::IMREAD_UNCHANGED);
|
|
|
|
|
if (inImg.empty())
|
|
|
|
|
{
|
|
|
|
|
printf("[%zu/%zu] read fail: %s\n", i + 1, inList.size(), inPath.c_str());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 统一成模型要求的输入:单通道 8U、模型输入尺寸
|
|
|
|
|
if (inImg.channels() != nModelInCh)
|
|
|
|
|
{
|
|
|
|
|
cv::Mat tmp;
|
|
|
|
|
if (inImg.channels() >= 3)
|
|
|
|
|
{
|
|
|
|
|
cv::cvtColor(inImg, tmp, cv::COLOR_BGR2GRAY);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tmp = inImg.clone();
|
|
|
|
|
}
|
|
|
|
|
inImg = tmp;
|
|
|
|
|
}
|
|
|
|
|
if (inImg.type() != CV_8U)
|
|
|
|
|
{
|
|
|
|
|
inImg.convertTo(inImg, CV_8U);
|
|
|
|
|
}
|
|
|
|
|
if (inImg.cols != nModelInW || inImg.rows != nModelInH)
|
|
|
|
|
{
|
|
|
|
|
cv::resize(inImg, inImg, cv::Size(nModelInW, nModelInH));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv::Mat outImg;
|
|
|
|
|
int re = pAI_Model->AIDet(inImg, outImg);
|
|
|
|
|
if (re != 0 || outImg.empty())
|
|
|
|
|
{
|
|
|
|
|
printf("[%zu/%zu] infer fail(%d): %s\n", i + 1, inList.size(), re, inPath.c_str());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 输出 resize 回模型输入尺寸(与 AI_Detect_QX 保存的 out 图尺寸保持一致)
|
|
|
|
|
if (outImg.cols != nModelInW || outImg.rows != nModelInH)
|
|
|
|
|
{
|
|
|
|
|
cv::resize(outImg, outImg, cv::Size(nModelInW, nModelInH), 0, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 相对路径,并保持子目录结构
|
|
|
|
|
std::string rel = inPath.substr(strSrcRoot.size());
|
|
|
|
|
std::string outRel = rel;
|
|
|
|
|
const std::string inSuffix = "_in.png";
|
|
|
|
|
const std::string outSuffix = "_out.png";
|
|
|
|
|
size_t pos = outRel.rfind(inSuffix);
|
|
|
|
|
if (pos != std::string::npos)
|
|
|
|
|
{
|
|
|
|
|
outRel.replace(pos, inSuffix.size(), outSuffix);
|
|
|
|
|
}
|
|
|
|
|
std::string outPath = strDstRoot + outRel;
|
|
|
|
|
|
|
|
|
|
// 创建输出父目录
|
|
|
|
|
fs::path outParent = fs::path(outPath).parent_path();
|
|
|
|
|
if (!outParent.empty())
|
|
|
|
|
{
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::create_directories(outParent, ec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cv::imwrite(outPath, outImg))
|
|
|
|
|
{
|
|
|
|
|
nOK++;
|
|
|
|
|
printf("[%zu/%zu] ok: %s -> %s\n", i + 1, inList.size(), rel.c_str(), outRel.c_str());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printf("[%zu/%zu] save fail: %s\n", i + 1, inList.size(), outPath.c_str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("TestAIDet: complete, success %d / %zu\n", nOK, inList.size());
|
|
|
|
|
return 0;
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
/// @brief AI 检测独立测试:遍历 /home/aidlux/BOE/FOG/AI_Detect/ 下所有 *_in.png,
|
|
|
|
|
/// 送入 AI_defect_NF 模型推理,将输出 *_out.png 保存到 /home/aidlux/BOE/FOG/AI_Detect_TEST/
|
|
|
|
|
/// @return 0 表示成功
|
|
|
|
|
|