You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

322 lines
9.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* @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 <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sys/time.h>
#include <sys/stat.h>
#include <cerrno>
#include <cstddef>
#include <memory>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <deque>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace std;
// ============================================================================
// 异步存图器header-only 单例)
// 场景:检测热点循环里直接 cv::imwrite编码 + 写盘会把主流程拖慢
// CTcsCheck::AdaptiveBinary 逐块存残点调试图)。
// 做法:调用方把图 clone 后入队即返回,独立后台线程串行编码落盘,
// 调用方只付一次内存拷贝的代价,不再等磁盘 IO。
// 线程安全;队列有长度上限,写盘慢于入队时 Push 会阻塞(背压),避免内存暴涨。
// 实现放在头文件、不依赖 CheckUtil.cppTcsCheck 这类未链接 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<int> &vParams)
{
if (strPath.empty() || img.empty())
{
return;
}
ImgTask task;
task.strPath = strPath;
task.vParams = vParams;
task.mat = img.clone(); // 深拷贝,隔离调用方数据的生命周期
std::unique_lock<std::mutex> 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<std::mutex> lk(m_mtx);
m_cvIdle.wait(lk, [this] { return m_nPending == 0; });
}
// 队列长度上限(张);调大后唤醒被背压阻塞的入队方
void SetQueueLimit(std::size_t nLimit)
{
{
std::lock_guard<std::mutex> lk(m_mtx);
m_nQueueLimit = nLimit;
}
m_cvHasRoom.notify_all();
}
// 关闭:排空剩余队列后退出后台线程(可重复调用)
void Stop()
{
{
std::lock_guard<std::mutex> 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<int> 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<std::mutex> 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<std::mutex> 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<ImgTask> 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<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);
// ===================== 异步存图(实现见文件顶部 CAsyncImgSaver =====================
// 入队即返回父目录不存在会在后台自动创建img 会被深拷贝,调用方可立即复用原图
static void SaveImgAsync(const std::string &strPath, const cv::Mat &img,
const std::vector<int> &vParams = std::vector<int>())
{
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 <typename... Args>
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<char[]> 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_