From cd95910651127beecbc8dc999dc9a24f8523878a Mon Sep 17 00:00:00 2001 From: liusiyang Date: Mon, 1 Jun 2026 10:07:20 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E6=B7=BB=E5=8A=A0ng=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../include/CameraCheckAnalysisy.hpp | 2 + AlgorithmModule/include/CheckUtil.hpp | 3 +- AlgorithmModule/src/CameraCheckAnalysisy.cpp | 199 ++++++++++++++++++ AlgorithmModule/src/CheckUtil.cpp | 92 ++++++++ 4 files changed, 295 insertions(+), 1 deletion(-) diff --git a/AlgorithmModule/include/CameraCheckAnalysisy.hpp b/AlgorithmModule/include/CameraCheckAnalysisy.hpp index 3cf235d..97df9ac 100644 --- a/AlgorithmModule/include/CameraCheckAnalysisy.hpp +++ b/AlgorithmModule/include/CameraCheckAnalysisy.hpp @@ -101,6 +101,8 @@ private: // 插入相机日志 int InsertCameraLog(); + int ngImgSave(); + ChannelCheckFunction *GetChannelFuntion(std::string strChannelName); // 获得 通道的检测功能 // 获取 检测核心库 diff --git a/AlgorithmModule/include/CheckUtil.hpp b/AlgorithmModule/include/CheckUtil.hpp index a24f405..946fc47 100644 --- a/AlgorithmModule/include/CheckUtil.hpp +++ b/AlgorithmModule/include/CheckUtil.hpp @@ -56,7 +56,8 @@ public: static std::string GetRectString(cv::Rect rect); // 创建目录 static int CreateDir(const std::string &dir); - + // 删除目录 + static int DeleteDir(const std::string &dir); // 点的距离是否过小 static bool bcalDis(cv::Point p1, cv::Point p2, int disT); static double calDis(cv::Point2f p1, cv::Point2f p2); diff --git a/AlgorithmModule/src/CameraCheckAnalysisy.cpp b/AlgorithmModule/src/CameraCheckAnalysisy.cpp index 58d996e..d6255a1 100644 --- a/AlgorithmModule/src/CameraCheckAnalysisy.cpp +++ b/AlgorithmModule/src/CameraCheckAnalysisy.cpp @@ -17,6 +17,17 @@ #include "CameraCheckAnalysisy.hpp" #include "CheckUtil.hpp" #include "Define.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static std::mutex g_ngImgSave_mutex; +static std::vector> g_ngImgSave_futures; CameraCheckAnalysisy::CameraCheckAnalysisy() { @@ -1033,6 +1044,7 @@ int CameraCheckAnalysisy::CheckImgRun() 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()); InsertCameraLog(); + ngImgSave(); std::lock_guard lock(mtx_WaiteImg); m_bHaveImgeDet = false; @@ -1173,6 +1185,193 @@ int CameraCheckAnalysisy::InsertCameraLog() } return 0; } + +int writeLog(std::string strSavePath, std::vector logList) +{ + std::string str = strSavePath + ".txt"; + // 打开文件 + std::ofstream outFile(str); + + // 检查文件是否成功打开 + if (!outFile) + { + std::cerr << "Failed to open file." << std::endl; + return 1; + } + + // 将vector中的内容写入文件 + for (const auto &str : logList) + { + // printf("%s \n",str.c_str()); + outFile << str << std::endl; + } + + // 关闭文件 + outFile.close(); + + return 0; +} + +static std::string SanitizePathComponent(const std::string& input) { + std::string result; + result.reserve(input.size()); + for (char ch : input) { + // 只允许字母、数字、下划线、点、短横线,其余替换为下划线 + if (isalnum(static_cast(ch)) || ch == '_' || ch == '.' || ch == '-') { + result.push_back(ch); + } else { + result.push_back('_'); + } + } + // 防止相对路径伪装(如 ".", "..") + if (result == "." || result == "..") { + result = "_"; + } + return result; +} + +int CameraCheckAnalysisy::ngImgSave() { + // 拷贝智能指针,保证在异步任务中引用计数有效 + auto result_copy = m_pCheck_Result; + std::string save_path_root = "/home/aidlux/BOE/CELL_ET/cloud/"; + + auto task = std::async(std::launch::async, [result_copy, save_path_root]() { + try { + if (!result_copy) return; + + // ---------- 目录清理(加锁防止并发删除)---------- + { + static std::mutex cleanup_mutex; // 静态锁,所有实例共享 + std::lock_guard lock(cleanup_mutex); + + DIR* dir = opendir(save_path_root.c_str()); + if (dir) { + std::vector> dirs; + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + if (name == "." || name == "..") continue; + std::string full_path = save_path_root + name; + struct stat st; + if (stat(full_path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) { + struct stat lst; + if (lstat(full_path.c_str(), &lst) == 0 && !S_ISLNK(lst.st_mode)) { + dirs.emplace_back(full_path, st.st_mtime); + } + } + } + closedir(dir); + + if (dirs.size() > 100) { + std::sort(dirs.begin(), dirs.end(), + [](auto& a, auto& b) { return a.second < b.second; }); + const std::string& oldest_dir = dirs.front().first; + if (!oldest_dir.empty() && oldest_dir != save_path_root) { + CheckUtil::DeleteDir(oldest_dir); + } + } + } else { + cerr << ("ERROR: cannot open root directory for cleanup"); + } + } + + // ---------- 保存NG图像 ---------- + int ImageNum = static_cast(result_copy->ImageALLDetResultList.size()); + for (int imgidx = 0; imgidx < ImageNum; ++imgidx) { + auto pImageResult = result_copy->ImageALLDetResultList.at(imgidx); + if (!pImageResult) continue; + auto m_CheckResult_shareP = pImageResult->result; + if (!m_CheckResult_shareP) continue; + + // 提取并净化路径组件 + std::string raw_product = result_copy->productBaseResult ? + result_copy->productBaseResult->strproductName : "unknown"; + std::string raw_camera = result_copy->cameraBaseResult ? + result_copy->cameraBaseResult->strCameraName : "unknown"; + std::string raw_channel = m_CheckResult_shareP->basicResult.strChannel; + + std::string safe_product = SanitizePathComponent(raw_product); + std::string safe_camera = SanitizePathComponent(raw_camera); + std::string safe_channel = SanitizePathComponent(raw_channel); + + // 只保存NG结果且图像数据非空 + if ((m_CheckResult_shareP->nresult == 1 || safe_channel == "L255") && + !m_CheckResult_shareP->resultMaskImg.empty() && + !m_CheckResult_shareP->resultimg.empty() && + !m_CheckResult_shareP->cutSrcimg.empty()) { + + // 构建保存目录(使用净化后的组件) + std::string save_dir = save_path_root + "/" + safe_product + "/" + safe_camera + "/"; + if (CheckUtil::CreateDir(save_dir) != 0) { + cerr << ("Failed to create directory: " + save_dir); + continue; + } + + // 深拷贝图像数据,避免原始数据被其他线程修改 + cv::Mat mask_img = m_CheckResult_shareP->resultMaskImg.clone(); + cv::Mat result_img = m_CheckResult_shareP->resultimg.clone(); + cv::Mat cut_img = m_CheckResult_shareP->cutSrcimg.clone(); + + try { + // cv::imwrite(save_dir + safe_channel + "_AIMask.png", mask_img); + // cv::imwrite(save_dir + safe_channel + "_Resultimg.png", result_img); + // cv::imwrite(save_dir + safe_channel + "_CutImg.png", cut_img); + writeLog(save_dir+ safe_channel + "log", m_CheckResult_shareP->det_LogList); + } catch (const cv::Exception& e) { + cerr << ("OpenCV exception: " + std::string(e.what())); + } catch (const std::exception& e) { + cerr << ("Standard exception: " + std::string(e.what())); + } catch (...) { + cerr << ("Unknown exception during image save"); + } + } + } + } catch (const std::exception& e) { + cerr << ("Future get exception: " + std::string(e.what())); + } catch (...) { + cerr << ("Future get unknown exception"); + } + }); + + // ---------- 管理 future 队列(清理已完成任务)---------- + { + std::lock_guard lock(g_ngImgSave_mutex); + g_ngImgSave_futures.emplace_back(std::move(task)); + + size_t write_idx = 0; + for (size_t i = 0; i < g_ngImgSave_futures.size(); ++i) { + auto& f = g_ngImgSave_futures[i]; + // 检查任务是否已就绪(非阻塞) + if (f.valid() && f.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { + try { + f.get(); + } catch (const std::exception& e) { + cerr << ("Future get exception: " + std::string(e.what())); + } catch (...) { + cerr << ("Future get unknown exception"); + } + } else { + // 保留未完成的任务 + if (i != write_idx) { + g_ngImgSave_futures[write_idx] = std::move(g_ngImgSave_futures[i]); + } + ++write_idx; + } + } + g_ngImgSave_futures.resize(write_idx); + + // 限制队列最大长度,防止无限堆积(例如超过100则丢弃最老的任务) + const size_t MAX_PENDING_TASKS = 20; + if (g_ngImgSave_futures.size() > MAX_PENDING_TASKS) { + // 丢弃最早未完成的任务(注意:需要确保其可被销毁,不阻塞) + g_ngImgSave_futures.erase(g_ngImgSave_futures.begin()); + } + } + + return 0; +} + + ChannelCheckFunction *CameraCheckAnalysisy::GetChannelFuntion(std::string strChannelName) { ChannelCheckFunction *p = NULL; diff --git a/AlgorithmModule/src/CheckUtil.cpp b/AlgorithmModule/src/CheckUtil.cpp index c9f57fa..7b247f8 100644 --- a/AlgorithmModule/src/CheckUtil.cpp +++ b/AlgorithmModule/src/CheckUtil.cpp @@ -20,6 +20,8 @@ #include #include #include "snowflake.hpp" +#include +#include int _sysmkdir_2(const std::string &dir) { @@ -39,6 +41,57 @@ int _sysmkdir_2(const std::string &dir) } return 0; } +static int _remove_dir_recursive_(const std::string &dir_path) { + DIR *dir = opendir(dir_path.c_str()); + if (!dir) { + // 如果目录不存在,视为成功(因为目标是不存在) + if (errno == ENOENT) return 0; + printf("opendir[%s] error: %s\n", dir_path.c_str(), strerror(errno)); + return -1; + } + + struct dirent *entry; + while ((entry = readdir(dir)) != nullptr) { + std::string name = entry->d_name; + // 跳过 . 和 .. + if (name == "." || name == "..") { + continue; + } + + std::string full_path = dir_path + "/" + name; + struct stat st; + + // 获取文件状态,使用 lstat 以处理符号链接 + if (lstat(full_path.c_str(), &st) == -1) { + printf("lstat[%s] error: %s\n", full_path.c_str(), strerror(errno)); + continue; + } + + // 如果是目录,递归删除 + if (S_ISDIR(st.st_mode)) { + if (_remove_dir_recursive_(full_path) != 0) { + closedir(dir); + return -1; + } + // 删除空子目录 + if (rmdir(full_path.c_str()) != 0) { + printf("rmdir[%s] error: %s\n", full_path.c_str(), strerror(errno)); + closedir(dir); + return -1; + } + } else { + // 如果是文件(或符号链接等),直接删除 + if (remove(full_path.c_str()) != 0) { + printf("remove[%s] error: %s\n", full_path.c_str(), strerror(errno)); + closedir(dir); + return -1; + } + } + } + + closedir(dir); + return 0; +} std::string __getParentDir_2(const std::string &dir) { std::string pdir = dir; @@ -506,6 +559,45 @@ int CheckUtil::CreateDir(const std::string &dir) return ret; } +int CheckUtil::DeleteDir(const std::string &dir) +{ + if (dir.empty()) { + return -1; + } + + // 检查路径是否存在 + struct stat st; + if (stat(dir.c_str(), &st) != 0) { + // 目录不存在,视为成功 + return 0; + } + + // 如果不是目录,可能是文件,尝试直接删除 + if (!S_ISDIR(st.st_mode)) { + if (remove(dir.c_str()) == 0) { + return 0; + } else { + printf("remove file[%s] error: %s\n", dir.c_str(), strerror(errno)); + return -1; + } + } + + // 是目录,先递归清空内容 + if (_remove_dir_recursive_(dir) != 0) { + return -1; + } + + // 最后删除空目录本身 + if (rmdir(dir.c_str()) != 0) { + printf("rmdir[%s] error: %s\n", dir.c_str(), strerror(errno)); + return -1; + } + + // printf("remove dir[%s] success.\n", dir.c_str()); + return 0; +} + + 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));