/* * @Descripttion: * @version: * @Author: sueRimn * @Date: 2022-04-28 10:41:42 * @LastEditors: xiewenji 527774126@qq.com * @LastEditTime: 2025-08-03 21:32:02 */ /* * FileName:CoreLogicFactory.hpp * Version:V1.0 * Description: * Created On:Mon Sep 10 11:13:13 UTC 2018 * Modified date: * Author:Sky */ #ifndef _CheckUtil_HPP_ #define _CheckUtil_HPP_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; // ============================================================================ // 异步存图器(header-only 单例) // 场景:检测热点循环里直接 cv::imwrite,编码 + 写盘会把主流程拖慢 // (例:CTcsCheck::AdaptiveBinary 逐块存残点调试图)。 // 做法:调用方把图 clone 后入队即返回,独立后台线程串行编码落盘, // 调用方只付一次内存拷贝的代价,不再等磁盘 IO。 // 线程安全;队列有长度上限,写盘慢于入队时 Push 会阻塞(背压),避免内存暴涨。 // 实现放在头文件、不依赖 CheckUtil.cpp:TcsCheck 这类未链接 AlgorithmModule // 的模块也能直接使用(否则会形成反向链接依赖)。 // ============================================================================ class CAsyncImgSaver { public: static CAsyncImgSaver &Instance() { static CAsyncImgSaver s_inst; // C++11 起局部静态初始化线程安全 return s_inst; } // 入队一张图(img 会被 clone,返回后调用方可立即释放/复用原图) void Push(const std::string &strPath, const cv::Mat &img, const std::vector &vParams) { if (strPath.empty() || img.empty()) { return; } ImgTask task; task.strPath = strPath; task.vParams = vParams; task.mat = img.clone(); // 深拷贝,隔离调用方数据的生命周期 std::unique_lock lk(m_mtx); if (m_bStop) { return; // 已关闭:直接丢弃,避免后台线程退出后无限等待 } if (m_nQueueLimit > 0) { // 队列满则阻塞等待(背压),顺带让 cv::Mat 内存占用可控 m_cvHasRoom.wait(lk, [this] { return m_bStop || m_deqTask.size() < m_nQueueLimit; }); if (m_bStop) { return; } } m_deqTask.push_back(std::move(task)); ++m_nPending; lk.unlock(); m_cvHasTask.notify_one(); } // 等待队列中(含正在写盘)的图全部落盘 void WaitIdle() { std::unique_lock lk(m_mtx); m_cvIdle.wait(lk, [this] { return m_nPending == 0; }); } // 队列长度上限(张);调大后唤醒被背压阻塞的入队方 void SetQueueLimit(std::size_t nLimit) { { std::lock_guard lk(m_mtx); m_nQueueLimit = nLimit; } m_cvHasRoom.notify_all(); } // 关闭:排空剩余队列后退出后台线程(可重复调用) void Stop() { { std::lock_guard lk(m_mtx); m_bStop = true; } m_cvHasTask.notify_all(); m_cvHasRoom.notify_all(); if (m_thread.joinable()) { m_thread.join(); } } private: struct ImgTask { std::string strPath; cv::Mat mat; std::vector vParams; }; CAsyncImgSaver() : m_bStop(false) , m_nQueueLimit(4096) // 默认最多积压 4096 张待写图 , m_nPending(0) { m_thread = std::thread(&CAsyncImgSaver::Worker, this); } ~CAsyncImgSaver() { Stop(); // 进程退出兜底:把队列里剩余的图写完再退 } CAsyncImgSaver(const CAsyncImgSaver &) = delete; CAsyncImgSaver &operator=(const CAsyncImgSaver &) = delete; // 后台线程主循环:串行取图 -> 建目录 -> 编码落盘 void Worker() { std::string strLastDir; // 缓存上次建好的目录,避免每张图都做一次 mkdir while (true) { ImgTask task; { std::unique_lock lk(m_mtx); m_cvHasTask.wait(lk, [this] { return m_bStop || !m_deqTask.empty(); }); if (m_deqTask.empty()) { return; // 收到停止信号且队列已排空 } task = std::move(m_deqTask.front()); m_deqTask.pop_front(); } m_cvHasRoom.notify_one(); // 队列腾出空位,唤醒可能被阻塞的入队方 if (!task.mat.empty()) { EnsureParentDir(task.strPath, strLastDir); try { cv::imwrite(task.strPath, task.mat, task.vParams); } catch (const cv::Exception &e) { std::cerr << "[SaveImgAsync] imwrite failed: " << task.strPath << " : " << e.what() << std::endl; } } { std::lock_guard lk(m_mtx); if (m_nPending > 0) { --m_nPending; } if (m_nPending == 0) { m_cvIdle.notify_all(); } } } } // 递归创建多级目录(已存在视为成功) static bool MakeDirs(const std::string &strPath) { if (strPath.empty()) { return false; } if (mkdir(strPath.c_str(), 0755) == 0 || errno == EEXIST) { return true; } const std::size_t pos = strPath.find_last_of('/'); if (pos == std::string::npos || pos == 0) { return false; } if (!MakeDirs(strPath.substr(0, pos))) { return false; } return (mkdir(strPath.c_str(), 0755) == 0 || errno == EEXIST); } // 创建文件所在目录;strCacheDir 命中则省掉一次 mkdir static void EnsureParentDir(const std::string &strPath, std::string &strCacheDir) { const std::size_t pos = strPath.find_last_of('/'); if (pos == std::string::npos || pos == 0) { return; // 没有目录部分(相对文件名) } const std::string strDir = strPath.substr(0, pos); if (strDir == strCacheDir) { return; } MakeDirs(strDir); strCacheDir = strDir; } std::thread m_thread; std::mutex m_mtx; std::condition_variable m_cvHasTask; // 有新任务 std::condition_variable m_cvHasRoom; // 队列有空位 std::condition_variable m_cvIdle; // 全部落盘 std::deque m_deqTask; bool m_bStop; std::size_t m_nQueueLimit; std::size_t m_nPending; // 已入队但尚未写完的数量(含正在写) }; class CheckUtil { public: static long getcurTime(); static std::string getCurrentDate(); static std::string getCurTimeHMS(); static std::string Op_float2String(float nvalue); static int64_t getSnowId(); 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 compareIgnoreCase(const std::string &str1, const std::string &str2); static bool RoiInImg(cv::Rect roi, cv::Mat img); static int printROI(cv::Rect roi, std::string str = ""); static float CalIoU(cv::Rect rect1, cv::Rect rect2); static float CalIoU_t(cv::Rect rect1, cv::Rect rect2); static int CheckRect(cv::Rect &roi, int img_w, int img_h); 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 CalImgBrightness(cv::Mat imgRoi); // 计算角度 static float Cal2PointAngle(cv::Point p_left, cv::Point p_right); // 找图片的最大外轮廓 static cv::Rect getLargestContourROI(const cv::Mat &binaryImg, bool &found); static std::string GetRectString(cv::Rect rect); //创建目录 static int CreateDir(const std::string &dir); // 点的距离是否过小 static bool bcalDis(cv::Point p1, cv::Point p2, int disT); static double calDis(cv::Point p1, cv::Point p2); static void PrintRect(cv::Rect roi, std::string str = ""); static int cutSmallImg(cv::Mat img, std::vector &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); // ===================== 异步存图(实现见文件顶部 CAsyncImgSaver) ===================== // 入队即返回,父目录不存在会在后台自动创建;img 会被深拷贝,调用方可立即复用原图 static void SaveImgAsync(const std::string &strPath, const cv::Mat &img, const std::vector &vParams = std::vector()) { CAsyncImgSaver::Instance().Push(strPath, img, vParams); } // 等待队列里所有异步存图落盘(退出/校验结果前调用) static void WaitSaveImgDone() { CAsyncImgSaver::Instance().WaitIdle(); } // 队列上限(张),默认 4096;超限时 SaveImgAsync 会阻塞(背压),0 表示不限制 static void SetSaveImgQueueLimit(std::size_t nLimit) { CAsyncImgSaver::Instance().SetQueueLimit(nLimit); } // 主动关闭后台存图线程(排空剩余队列后退出);不调用也会在进程退出时自动收尾 static void StopSaveImg() { CAsyncImgSaver::Instance().Stop(); } }; // 无参数重载:直接返回原字符串,避免 -Wformat-security 警告 static inline std::string str_Format(const std::string &format) { return format; } template static std::string str_Format(const std::string &format, Args... args) { auto size_buf = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; std::unique_ptr buf(new (std::nothrow) char[size_buf]); if (!buf) return std::string(""); std::snprintf(buf.get(), size_buf, format.c_str(), args...); return std::string(buf.get(), buf.get() + size_buf - 1); } #endif //_CORELOGICFACTORY_HPP_