|
|
/*
|
|
|
* @Author: your name
|
|
|
* @Date: 2022-04-20 15:50:00
|
|
|
* @LastEditTime: 2025-09-23 11:34:53
|
|
|
* @LastEditors: xiewenji 527774126@qq.com
|
|
|
* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
|
* @FilePath: /ZCXD_MonitorPlatform/src/CoreLogicModule/src/CamDeal.cpp
|
|
|
*/
|
|
|
#include "ImgCheckAnalysisy.hpp"
|
|
|
#include "CheckUtil.hpp"
|
|
|
#include "Define.h"
|
|
|
#include <omp.h>
|
|
|
#include "AICommonDefine.h"
|
|
|
#include "AlignShare.h"
|
|
|
#include <algorithm>
|
|
|
#include <cctype>
|
|
|
|
|
|
// 把手判定参数:
|
|
|
// 每个非空的"把手区域"附近都应该有一个把手:凸起宽高与该区域外接矩形宽高的比值落在下面范围内才算匹配
|
|
|
#define HANDLE_SIZE_MIN_RATIO 0.8f
|
|
|
#define HANDLE_SIZE_MAX_RATIO 1.2f
|
|
|
// "附近"的搜索范围:把手区域外接矩形在宽/高方向各外扩该比例
|
|
|
#define HANDLE_NEAR_EXPAND_RATIO 5
|
|
|
// 把手缺失检测开关见 Function_Support_Det::bDetHandle(CheckConfigDefine.h)
|
|
|
|
|
|
// 用于排序轮廓的比较函数
|
|
|
static bool compareContourAreas(const vector<Point> &contour1, const vector<Point> &contour2)
|
|
|
{
|
|
|
double i = contourArea(contour1);
|
|
|
double j = contourArea(contour2);
|
|
|
return (i > j);
|
|
|
}
|
|
|
|
|
|
// 2x3 仿射矩阵小工具:变换点、矩形外接、组合矩阵(检测图裁剪坐标映射用)
|
|
|
static cv::Point2f MatAffinePoint(const cv::Mat &mat32, const cv::Point2f &pt)
|
|
|
{
|
|
|
if (mat32.empty() || mat32.rows != 2 || mat32.cols != 3)
|
|
|
{
|
|
|
return pt;
|
|
|
}
|
|
|
const double *m = mat32.ptr<double>(0);
|
|
|
return cv::Point2f((float)(m[0] * pt.x + m[1] * pt.y + m[2]),
|
|
|
(float)(m[3] * pt.x + m[4] * pt.y + m[5]));
|
|
|
}
|
|
|
|
|
|
// 矩形四角经仿射变换后取外接矩形
|
|
|
static cv::Rect MatAffineRect(const cv::Mat &mat32, const cv::Rect &roi)
|
|
|
{
|
|
|
std::vector<cv::Point2f> pts(4);
|
|
|
pts[0] = MatAffinePoint(mat32, cv::Point2f((float)roi.x, (float)roi.y));
|
|
|
pts[1] = MatAffinePoint(mat32, cv::Point2f((float)(roi.x + roi.width), (float)roi.y));
|
|
|
pts[2] = MatAffinePoint(mat32, cv::Point2f((float)(roi.x + roi.width), (float)(roi.y + roi.height)));
|
|
|
pts[3] = MatAffinePoint(mat32, cv::Point2f((float)roi.x, (float)(roi.y + roi.height)));
|
|
|
|
|
|
float fminX = pts[0].x, fmaxX = pts[0].x, fminY = pts[0].y, fmaxY = pts[0].y;
|
|
|
for (int i = 1; i < 4; i++)
|
|
|
{
|
|
|
if (pts[i].x < fminX)
|
|
|
fminX = pts[i].x;
|
|
|
if (pts[i].x > fmaxX)
|
|
|
fmaxX = pts[i].x;
|
|
|
if (pts[i].y < fminY)
|
|
|
fminY = pts[i].y;
|
|
|
if (pts[i].y > fmaxY)
|
|
|
fmaxY = pts[i].y;
|
|
|
}
|
|
|
int x0 = cvFloor(fminX);
|
|
|
int y0 = cvFloor(fminY);
|
|
|
int x1 = cvCeil(fmaxX);
|
|
|
int y1 = cvCeil(fmaxY);
|
|
|
return cv::Rect(x0, y0, x1 - x0, y1 - y0);
|
|
|
}
|
|
|
|
|
|
// 组合仿射:先应用 mB 再应用 mA,即 mA ∘ mB,结果仍为 2x3
|
|
|
static cv::Mat MatAffineCompose(const cv::Mat &mA, const cv::Mat &mB)
|
|
|
{
|
|
|
if (mA.empty() || mA.rows != 2 || mA.cols != 3)
|
|
|
{
|
|
|
return mB.clone();
|
|
|
}
|
|
|
if (mB.empty() || mB.rows != 2 || mB.cols != 3)
|
|
|
{
|
|
|
return mA.clone();
|
|
|
}
|
|
|
cv::Mat hA = cv::Mat::eye(3, 3, CV_64F);
|
|
|
cv::Mat hB = cv::Mat::eye(3, 3, CV_64F);
|
|
|
mA.copyTo(hA(cv::Rect(0, 0, 3, 2)));
|
|
|
mB.copyTo(hB(cv::Rect(0, 0, 3, 2)));
|
|
|
cv::Mat hC = hA * hB;
|
|
|
return hC(cv::Rect(0, 0, 3, 2)).clone();
|
|
|
}
|
|
|
|
|
|
ImgCheckAnalysisy::ImgCheckAnalysisy()
|
|
|
: m_pAI_Edge_Algin(m_pdetlog)
|
|
|
{
|
|
|
|
|
|
m_nErrorCode = CHECK_OK;
|
|
|
m_nThreadIdx = -1;
|
|
|
m_bInitSucc = false;
|
|
|
m_bExit = false;
|
|
|
m_nRun_Status = CHECK_THREAD_STATUS_IDLE;
|
|
|
|
|
|
m_fImgage_Scale_X = 0.03f;
|
|
|
m_fImgage_Scale_Y = 0.03f;
|
|
|
m_pBasicConfig = NULL;
|
|
|
|
|
|
m_strCurDetChannel = "";
|
|
|
m_bupdateconfig = false;
|
|
|
m_pbaseCheckFunction = &m_AnalysisyConfig.baseFunction;
|
|
|
|
|
|
m_strLastDate = "";
|
|
|
m_strRootPath_TA_cls = "/home/aidlux/BOE/Cls/TA/";
|
|
|
m_strRootPath_CA_cls = "/home/aidlux/BOE/Cls/CA/";
|
|
|
creatsavedir();
|
|
|
m_pImageStorage = ImageStorage::getInstance();
|
|
|
m_nConfigIdx = -1;
|
|
|
m_ImgBlobHFlagData = NULL;
|
|
|
}
|
|
|
|
|
|
ImgCheckAnalysisy::~ImgCheckAnalysisy()
|
|
|
{
|
|
|
ExitSystem();
|
|
|
if (m_ImgBlobHFlagData)
|
|
|
{
|
|
|
delete[] m_ImgBlobHFlagData;
|
|
|
m_ImgBlobHFlagData = NULL;
|
|
|
}
|
|
|
}
|
|
|
int ImgCheckAnalysisy::UpdateConfig(void *pconfig, int nConfigType)
|
|
|
{
|
|
|
int re = 0;
|
|
|
switch (nConfigType)
|
|
|
{
|
|
|
case CHECK_CONFIG_Run:
|
|
|
re = LoadRunConfig(pconfig);
|
|
|
if (re == 0)
|
|
|
{
|
|
|
// printf("---> LoadRunConfig Succ\n");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
printf("---> LoadRunConfig Fail\n");
|
|
|
}
|
|
|
break;
|
|
|
case CHECK_CONFIG_Module:
|
|
|
re = LoadCheckConfig(pconfig);
|
|
|
if (re == 0)
|
|
|
{
|
|
|
// printf("---> LoadAnalysisConfig Succ\n");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
printf("---> LoadAnalysisConfig Fail\n");
|
|
|
}
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
return re;
|
|
|
}
|
|
|
int ImgCheckAnalysisy::RunStart(void *pconfig1)
|
|
|
{
|
|
|
|
|
|
// 1 、更新参数 并判断参数是否合法
|
|
|
int re = CHECK_OK;
|
|
|
re = SetNewConfig();
|
|
|
if (CHECK_OK != re)
|
|
|
{
|
|
|
m_nErrorCode = re;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
// printf("---> RunStart Start m_RunConfig.nThreadIdx %d \n", m_RunConfig.nThreadIdx);
|
|
|
|
|
|
m_nThreadIdx = m_RunConfig.nThreadIdx;
|
|
|
|
|
|
re = InitRun(m_RunConfig.nCpu_start_Idx);
|
|
|
if (CHECK_OK != re)
|
|
|
{
|
|
|
m_nErrorCode = re;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
runner = std::make_shared<AIMulThreadRunBase>();
|
|
|
runner->Start();
|
|
|
|
|
|
m_nErrorCode = CHECK_OK;
|
|
|
// printf("ImgCheckAnalysisy >>>> ImgCheckThread %d Start Succ \n", m_nThreadIdx);
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::SetDataRun_SharePtr(std::shared_ptr<ImageAllResult> p)
|
|
|
{
|
|
|
// 设置正在检测
|
|
|
|
|
|
m_pImageAllResult = p;
|
|
|
DetImgInfo_shareP = p->result->in_shareImage;
|
|
|
m_CheckResult_shareP = p->result;
|
|
|
m_pImageAllResult->setStep(ImageAllResult::DetStep_Deting);
|
|
|
m_pdetlog = p->detlog;
|
|
|
m_pDetResult = p->pDetResult;
|
|
|
StartCheck();
|
|
|
m_nErrorCode = CHECK_OK;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::GetCheckReuslt(std::shared_ptr<ImageDetResult> &pResult)
|
|
|
{
|
|
|
|
|
|
m_CheckResult_shareP.reset();
|
|
|
DetImgInfo_shareP.reset();
|
|
|
SetIDLE();
|
|
|
// m_nErrorCode = CHECK_OK;
|
|
|
// printf("4 DetImgInfo_shareP count %ld m_nCheckResultErrorCode %d \n", DetImgInfo_shareP.use_count(), m_nCheckResultErrorCode);
|
|
|
return m_nCheckResultErrorCode;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::CheckImg(std::shared_ptr<ImageDetconfig> p, std::shared_ptr<ImageDetResult> &pResult)
|
|
|
{
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::ReJsonResul(std::shared_ptr<ImageDetconfig> p, std::shared_ptr<ImageDetResult> &pResult)
|
|
|
{
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::InitRun(int nId)
|
|
|
{
|
|
|
|
|
|
int re = CHECK_OK;
|
|
|
if (m_bInitSucc)
|
|
|
{
|
|
|
return CHECK_OK;
|
|
|
}
|
|
|
InitModel();
|
|
|
AI_Factory = AIFactory::GetInstance();
|
|
|
|
|
|
m_nRun_Status = CHECK_THREAD_STATUS_IDLE;
|
|
|
re = StartThread(nId);
|
|
|
if (CHECK_OK != re)
|
|
|
{
|
|
|
return re;
|
|
|
}
|
|
|
m_bInitSucc = true;
|
|
|
return re;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::GetStatus()
|
|
|
{
|
|
|
return m_nRun_Status;
|
|
|
}
|
|
|
|
|
|
std::string ImgCheckAnalysisy::GetVersion()
|
|
|
{
|
|
|
return std::string("BOE_1.7.91");
|
|
|
}
|
|
|
|
|
|
std::string ImgCheckAnalysisy::GetErrorInfo()
|
|
|
{
|
|
|
std::string str = GetErrorCodeInfo(m_nErrorCode);
|
|
|
printf("%s\n", str.c_str());
|
|
|
return str;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::creatsavedir()
|
|
|
{
|
|
|
std::string curDate = CheckUtil::getCurrentDate();
|
|
|
if (curDate == m_strLastDate)
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
|
m_strLastDate = curDate;
|
|
|
m_strRootPath_TA_cls += m_strLastDate + "/";
|
|
|
m_strRootPath_CA_cls += m_strLastDate + "/";
|
|
|
for (int i = 0; i < AI_CLass_QX_NAME_count; i++)
|
|
|
{
|
|
|
CheckUtil::CreateDir(m_strRootPath_TA_cls + std::to_string(i) + "/");
|
|
|
CheckUtil::CreateDir(m_strRootPath_CA_cls + std::to_string(i) + "/");
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void ImgCheckAnalysisy::SetTplInfo(const cv::RotatedRect &outerRect)
|
|
|
{
|
|
|
m_tplOuterRect = outerRect;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::LoadRunConfig(void *p)
|
|
|
{
|
|
|
if (p == NULL)
|
|
|
{
|
|
|
m_nErrorCode = CHECK_ERROR_Config_Null;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
RunInfoST *pconfig = (RunInfoST *)p;
|
|
|
m_RunConfig.copy(*pconfig);
|
|
|
|
|
|
return CHECK_OK;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::LoadCheckConfig(void *p)
|
|
|
{
|
|
|
if (p == NULL)
|
|
|
{
|
|
|
m_nErrorCode = CHECK_ERROR_Config_Null;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
m_pConfig = (ConfigBase *)p;
|
|
|
m_nConfigIdx = m_pConfig->GetConfigIdx();
|
|
|
m_nErrorCode = CHECK_OK;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
|
|
|
// 开启检测
|
|
|
int ImgCheckAnalysisy::StartCheck()
|
|
|
{
|
|
|
m_nRun_Status = CHECK_THREAD_STATUS_READY;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::SetIDLE()
|
|
|
{
|
|
|
// 更新参数
|
|
|
SetNewConfig();
|
|
|
m_nRun_Status = CHECK_THREAD_STATUS_IDLE;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::StartThread(int nId)
|
|
|
{
|
|
|
// 开启检测线程
|
|
|
ptr_thread_Run = std::make_shared<std::thread>(std::bind(&ImgCheckAnalysisy::Run, this, nId));
|
|
|
if (!m_RunConfig.bRetest)
|
|
|
{
|
|
|
ptr_thread_AI = std::make_shared<std::thread>(std::bind(&ImgCheckAnalysisy::ThreadTask, this, nId + 1));
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::StopThread()
|
|
|
{
|
|
|
m_bExit = true;
|
|
|
if (ptr_thread_Run != nullptr)
|
|
|
{
|
|
|
if (ptr_thread_Run->joinable())
|
|
|
{
|
|
|
ptr_thread_Run->join();
|
|
|
}
|
|
|
}
|
|
|
if (ptr_thread_AI != nullptr)
|
|
|
{
|
|
|
if (ptr_thread_AI->joinable())
|
|
|
{
|
|
|
ptr_thread_AI->join();
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::ExitSystem()
|
|
|
{
|
|
|
StopThread();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::InitModel()
|
|
|
{
|
|
|
|
|
|
// 获取当前gpu号确定的 AI处理线程
|
|
|
|
|
|
m_OtherDet_Config.nDeviceId = m_RunConfig.nDeviceId;
|
|
|
|
|
|
m_pAI_Edge_Algin.Init(&m_OtherDet_Config);
|
|
|
m_pAI_Edge_Algin.InitModel_ALL();
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
cv::Scalar ImgCheckAnalysisy::calc_blob_info_withstats(cv::Mat &img, const cv::Mat &mask, cv::Rect &stats, cv::Size k_size, int expand, double threshold)
|
|
|
{
|
|
|
|
|
|
// 解包 stats(x, y, w, h, area)
|
|
|
int x = stats.x, y = stats.y, w = stats.width, h = stats.height;
|
|
|
|
|
|
// 将图像转换为灰度图像
|
|
|
|
|
|
// 计算感兴趣区域 (ROI)
|
|
|
cv::Rect roi(x - expand, y - expand, w + 2 * expand, h + 2 * expand);
|
|
|
roi &= cv::Rect(0, 0, img.cols, img.rows); // 确保ROI在图像内
|
|
|
|
|
|
cv::Mat cimg = img(roi);
|
|
|
|
|
|
// cv::cvtColor(cimg, cimg, cv::COLOR_BGR2GRAY);
|
|
|
// return cv::Scalar(0, 0, 0);
|
|
|
|
|
|
cv::Mat cmask = mask(roi);
|
|
|
// getchar();
|
|
|
// 扩张掩膜
|
|
|
|
|
|
cv::Scalar mean_bk = cv::mean(cimg, ~cmask);
|
|
|
double fbk = mean_bk[0];
|
|
|
|
|
|
cv::Scalar mean_det = cv::mean(cimg, cmask);
|
|
|
double fdet = mean_det[0];
|
|
|
|
|
|
// 计算差异图像
|
|
|
cv::Mat diff = cv::abs(cimg - fbk);
|
|
|
|
|
|
// cv::imwrite("cimg.png",cimg);
|
|
|
// cv::imwrite("cmask.png",cmask);
|
|
|
// printf("%f %f - %f \n",fbk,fdet,fbk-fdet);
|
|
|
// getchar();
|
|
|
|
|
|
// diff = diff.mul(cmask > 0);
|
|
|
cv::Mat masked_image;
|
|
|
diff.copyTo(masked_image, cmask);
|
|
|
// 计算能量
|
|
|
double energy = cv::sum(masked_image)[0];
|
|
|
|
|
|
// 计算 hj(差异图像大于0的像素均值)
|
|
|
// double hj = std::abs(fbk - fdet);
|
|
|
// double hj = CheckUtil::CalHj(cimg, cmask, mean_bk.val[0]);
|
|
|
double hj = CheckUtil::CalHjWeighted(cimg, cmask, mean_bk.val[0], 2.0f);
|
|
|
|
|
|
int worb = 0;
|
|
|
if (fdet >= fbk)
|
|
|
{
|
|
|
worb = 1;
|
|
|
}
|
|
|
// cv::imwrite("cimg.png", cimg);
|
|
|
// cv::imwrite("cmask.png", cmask);
|
|
|
// cv::imwrite("diff.png", diff);
|
|
|
// cv::imwrite("masked_image.png", masked_image);
|
|
|
// printf("fbk %f fdet %f energy %f\n", fbk, fdet, energy);
|
|
|
// getchar();
|
|
|
|
|
|
return cv::Scalar(worb, energy, hj);
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::Adapt_Config(RotatedRect tplOuterRect, RotatedRect curOuterRect, Mat& det_img){
|
|
|
vector<cv::Point2f> cur_vertices = CheckUtil::sort_vertices(curOuterRect);
|
|
|
vector<cv::Point2f> tpl_vertices = CheckUtil::sort_vertices(tplOuterRect);
|
|
|
|
|
|
cv::Point2f src_pts[3] = {
|
|
|
cur_vertices[0], // 对应左上
|
|
|
cur_vertices[1], // 对应右上
|
|
|
cur_vertices[2] // 对应左下
|
|
|
};
|
|
|
cv::Point2f dst_pts[3] = {
|
|
|
tpl_vertices[0], // 左上
|
|
|
tpl_vertices[1], // 右上
|
|
|
tpl_vertices[2] // 左下
|
|
|
};
|
|
|
cv::Mat affine_mat = cv::getAffineTransform(src_pts, dst_pts);
|
|
|
|
|
|
// 仿射变换
|
|
|
cv::warpAffine(det_img, det_img, affine_mat, det_img.size());
|
|
|
|
|
|
// 将m_outer_rroi和m_inner_rroi变换到模板图像坐标系下
|
|
|
auto transformRotatedRect = [&affine_mat](cv::RotatedRect &rrect)
|
|
|
{
|
|
|
const double *M = affine_mat.ptr<double>(0);
|
|
|
|
|
|
cv::Point2f pts[4];
|
|
|
rrect.points(pts);
|
|
|
|
|
|
std::vector<cv::Point2f> transformed_pts(4);
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
{
|
|
|
float x = static_cast<float>(M[0] * pts[i].x + M[1] * pts[i].y + M[2]);
|
|
|
float y = static_cast<float>(M[3] * pts[i].x + M[4] * pts[i].y + M[5]);
|
|
|
transformed_pts[i] = cv::Point2f(x, y);
|
|
|
}
|
|
|
rrect = cv::minAreaRect(transformed_pts);
|
|
|
};
|
|
|
|
|
|
transformRotatedRect(m_outer_rroi);
|
|
|
transformRotatedRect(m_inner_rroi);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// 暗场复用明场定位:把字符串里的 "dark"(大小写不敏感)替换为 "bright";未命中返回空串
|
|
|
static std::string ReplaceDarkWithBright(const std::string &strIn)
|
|
|
{
|
|
|
const std::string strKey = ALIGN_REUSE_FOLLOWER_KEY; // "dark"
|
|
|
const std::string strSrc = ALIGN_REUSE_SOURCE_KEY; // "bright"
|
|
|
if (strIn.size() < strKey.size())
|
|
|
{
|
|
|
return "";
|
|
|
}
|
|
|
for (size_t i = 0; i + strKey.size() <= strIn.size(); i++)
|
|
|
{
|
|
|
size_t k = 0;
|
|
|
for (; k < strKey.size(); k++)
|
|
|
{
|
|
|
if (::tolower((unsigned char)strIn[i + k]) != (unsigned char)strKey[k])
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (k == strKey.size())
|
|
|
{
|
|
|
std::string strOut = strIn;
|
|
|
strOut.replace(i, strKey.size(), strSrc);
|
|
|
return strOut;
|
|
|
}
|
|
|
}
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
// ===== 暗场复用明场定位结果(ALIGN_REUSE_* 配置) =====
|
|
|
// 取当前图的产品ID/相机名:优先用 ImageAllResult 关联的基础结果,兕底用送图信息
|
|
|
void ImgCheckAnalysisy::GetProductCameraStr(std::string &strProduct, std::string &strCamera)
|
|
|
{
|
|
|
strProduct = "";
|
|
|
strCamera = "";
|
|
|
if (m_pImageAllResult)
|
|
|
{
|
|
|
if (m_pImageAllResult->productBaseResult)
|
|
|
{
|
|
|
strProduct = m_pImageAllResult->productBaseResult->strproductName;
|
|
|
}
|
|
|
if (m_pImageAllResult->cameraBaseResult)
|
|
|
{
|
|
|
strCamera = m_pImageAllResult->cameraBaseResult->strCameraName;
|
|
|
}
|
|
|
}
|
|
|
if (m_CheckResult_shareP && m_CheckResult_shareP->in_shareImage)
|
|
|
{
|
|
|
if (strProduct.empty())
|
|
|
{
|
|
|
strProduct = m_CheckResult_shareP->in_shareImage->strImgProductID;
|
|
|
}
|
|
|
if (strCamera.empty())
|
|
|
{
|
|
|
strCamera = m_CheckResult_shareP->in_shareImage->strCameraName;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 发布本图定位结果(成功)或失败标记(供同产品其它相机复用)
|
|
|
void ImgCheckAnalysisy::PublishAlignShare(bool bSucc)
|
|
|
{
|
|
|
std::string strProduct;
|
|
|
std::string strCamera;
|
|
|
GetProductCameraStr(strProduct, strCamera);
|
|
|
if (strProduct.empty() || strCamera.empty())
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
if (bSucc)
|
|
|
{
|
|
|
if (!m_pEdge_Align_Result)
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
cv::Size sz(0, 0);
|
|
|
if (m_CheckResult_shareP && m_CheckResult_shareP->in_shareImage)
|
|
|
{
|
|
|
sz = m_CheckResult_shareP->in_shareImage->img.size();
|
|
|
}
|
|
|
AlignShare::GetInstance().PublishSuccess(strProduct, strCamera, m_strCurDetChannel, m_pEdge_Align_Result, sz);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
AlignShare::GetInstance().PublishFailed(strProduct, strCamera, m_strCurDetChannel);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 暗场相机不做定位模型:等待并复用明场相机的定位结果
|
|
|
// 返回 true 时:m_pEdge_Align_Result / m_outer_rroi / m_inner_rroi 已用明场结果填充(标签检测独立执行)
|
|
|
// 返回 false:非复用相机,或复用失败(超时/源失败/无源图/尺寸不一致),调用方走自有定位
|
|
|
bool ImgCheckAnalysisy::TryReuseAlignFromSource(std::string &strSrcCamera, std::string &strSrcChannel, int &nWaitMs)
|
|
|
{
|
|
|
nWaitMs = -1;
|
|
|
#if !ALIGN_REUSE_ENABLE
|
|
|
return false;
|
|
|
#else
|
|
|
std::string strProduct;
|
|
|
std::string strCamera;
|
|
|
GetProductCameraStr(strProduct, strCamera);
|
|
|
|
|
|
// 是否复用由相机名决定(明/暗场是相机属性):相机名必须能按 dark->bright 替换
|
|
|
std::string strSrcCam = ReplaceDarkWithBright(strCamera);
|
|
|
if (strSrcCam.empty())
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
// 通道名可能不带 dark/bright(同一面的明/暗场相机共用一个通道名),此时沿用本相机通道名;
|
|
|
// 若带 dark 则同样替换,保证与源相机发布结果时的 key 一致
|
|
|
std::string strSrcChn = ReplaceDarkWithBright(m_strCurDetChannel);
|
|
|
if (strSrcChn.empty())
|
|
|
{
|
|
|
strSrcChn = m_strCurDetChannel;
|
|
|
}
|
|
|
strSrcCamera = strSrcCam;
|
|
|
strSrcChannel = strSrcChn;
|
|
|
|
|
|
if (!m_pImageAllResult || !m_pImageAllResult->productBaseResult || !m_pImageAllResult->productBaseResult->alignPushState)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align reuse", "reuse from %s|%s failed(no push state), do align by self", strSrcCam.c_str(), strSrcChn.c_str());
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align reuse", "wait %s|%s (timeout %d ms)", strSrcCam.c_str(), strSrcChn.c_str(), ALIGN_REUSE_WAIT_TIMEOUT_MS);
|
|
|
|
|
|
AlignShare &alignShare = AlignShare::GetInstance();
|
|
|
std::shared_ptr<AI_Edge_Algin::Edge_AI_Result> pSrc;
|
|
|
cv::Size srcSize;
|
|
|
const long t1 = CheckUtil::getcurTime();
|
|
|
bool bGot = false;
|
|
|
bool bSucc = false;
|
|
|
std::string strFailReason = "timeout";
|
|
|
while (true)
|
|
|
{
|
|
|
if (alignShare.TryGet(strProduct, strSrcCam, strSrcChn, bSucc, pSrc, srcSize))
|
|
|
{
|
|
|
bGot = true;
|
|
|
if (!bSucc)
|
|
|
{
|
|
|
strFailReason = "source align failed";
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
// 源图不会再来了(送图已结束,且源相机没有图):立即回退,不干等超时
|
|
|
if (!m_pImageAllResult->productBaseResult->alignPushState->MayStillCome(strSrcCam))
|
|
|
{
|
|
|
strFailReason = "no source image";
|
|
|
break;
|
|
|
}
|
|
|
if (CheckUtil::getcurTime() - t1 > ALIGN_REUSE_WAIT_TIMEOUT_MS)
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(ALIGN_REUSE_POLL_MS));
|
|
|
}
|
|
|
nWaitMs = (int)(CheckUtil::getcurTime() - t1);
|
|
|
|
|
|
if (!bGot || !bSucc)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align reuse", "reuse from %s|%s failed(wait %d ms, %s), do align by self",
|
|
|
strSrcCam.c_str(), strSrcChn.c_str(), nWaitMs, strFailReason.c_str());
|
|
|
return false;
|
|
|
}
|
|
|
if (!pSrc || pSrc->bigroi.empty())
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align reuse", "reuse from %s|%s failed(wait %d ms, src roi empty), do align by self",
|
|
|
strSrcCam.c_str(), strSrcChn.c_str(), nWaitMs);
|
|
|
return false;
|
|
|
}
|
|
|
// 图像尺寸必须与源图一致,否则源坐标系不可用
|
|
|
if (srcSize.width > 0 && srcSize.height > 0 && m_CheckResult_shareP && m_CheckResult_shareP->in_shareImage)
|
|
|
{
|
|
|
const cv::Mat &img = m_CheckResult_shareP->in_shareImage->img;
|
|
|
if (img.cols != srcSize.width || img.rows != srcSize.height)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Align reuse", "reuse from %s|%s failed, size %dx%d != cur %dx%d, do align by self",
|
|
|
strSrcCam.c_str(), strSrcChn.c_str(), srcSize.width, srcSize.height, img.cols, img.rows);
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 复用明场定位结果:ROI/掩码/映射都用明场的(标签检测由 CheckTagByBigMask 独立执行,不用明场的标签)
|
|
|
m_pEdge_Align_Result = pSrc;
|
|
|
m_outer_rroi = pSrc->bigroi[0];
|
|
|
if (!pSrc->smallroi.empty())
|
|
|
{
|
|
|
m_inner_rroi = pSrc->smallroi[0];
|
|
|
}
|
|
|
return true;
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::CheckRun()
|
|
|
{
|
|
|
|
|
|
// printf(">>>%s ================start \n", m_pImageAllResult->strBaseInfo.c_str());
|
|
|
|
|
|
long t1, t2, t3, t4, t5, t6, t7;
|
|
|
SetNewConfig();
|
|
|
t1 = CheckUtil::getcurTime();
|
|
|
|
|
|
CheckImgInit();
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
{
|
|
|
m_pdetlog->bPrintStr = true;
|
|
|
}
|
|
|
// m_pdetlog->bPrintStr = true;
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "1、basic Info", "---------------------------1、basic Info---------------------------------");
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Version", "%s", GetVersion().c_str());
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Updateconfig", "%d", m_bupdateconfig);
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Start", "%s", m_pImageAllResult->strBaseInfo.c_str());
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "ImgageScale", "Scale_X = %f Scale_Y = %f", m_fImgage_Scale_X, m_fImgage_Scale_Y);
|
|
|
m_bupdateconfig = false;
|
|
|
// 返回结果状态初始化
|
|
|
m_CheckResult_shareP->checkStatus = 1;
|
|
|
m_CheckResult_shareP->nresult = -1;
|
|
|
|
|
|
m_CheckResult_shareP->basicResult.img_id = m_CheckResult_shareP->in_shareImage->img_id;
|
|
|
m_CheckResult_shareP->basicResult.imgtype = m_CheckResult_shareP->in_shareImage->imgtype;
|
|
|
m_CheckResult_shareP->basicResult.imgstr = m_CheckResult_shareP->in_shareImage->imgstr;
|
|
|
m_CheckResult_shareP->basicResult.strChannel = m_CheckResult_shareP->in_shareImage->strChannel;
|
|
|
m_strCurDetChannel = m_CheckResult_shareP->basicResult.strChannel;
|
|
|
|
|
|
/*参数检查*/
|
|
|
int rec = ConfigCheck(DetImgInfo_shareP->img);
|
|
|
if (rec != CHECK_OK)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "ConfigCheck is error type = %d", rec);
|
|
|
PublishAlignShare(false); // 本图不会产出定位结果,通知可能的等待方(暗场复用)
|
|
|
m_nErrorCode = rec;
|
|
|
m_nCheckResultErrorCode = m_nErrorCode;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
long time_edge_s = CheckUtil::getcurTime();
|
|
|
|
|
|
m_pFuntion = GetChannelFuntion(m_strCurDetChannel);
|
|
|
int nfunction = 0;
|
|
|
if (m_pFuntion != NULL)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Detect function", "%s",
|
|
|
m_pFuntion->GetInfo("").c_str());
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
nfunction = 1;
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "m_pFuntion is NULL");
|
|
|
PublishAlignShare(false); // 本图不会产出定位结果,通知可能的等待方(暗场复用)
|
|
|
m_nErrorCode = 22;
|
|
|
m_nCheckResultErrorCode = m_nErrorCode;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
|
|
|
/*AI 边缘定位(内外边缘)*/
|
|
|
// 暗场相机不做定位模型,等待复用明场相机的定位结果(见 ALIGN_REUSE_* 配置);
|
|
|
// 非复用相机、或复用失败(源超时/源失败/无源图/尺寸不一致)时,仍走原有定位流程
|
|
|
int reedge = 0;
|
|
|
int nAlignReuseWaitMs = -1;
|
|
|
std::string strAlignSrcCamera;
|
|
|
std::string strAlignSrcChannel;
|
|
|
long time_edge_e;
|
|
|
if (TryReuseAlignFromSource(strAlignSrcCamera, strAlignSrcChannel, nAlignReuseWaitMs))
|
|
|
{
|
|
|
// 复用成功:m_pEdge_Align_Result / m_outer_rroi / m_inner_rroi 已用明场结果填充(标签检测独立执行,不复用明场标签)
|
|
|
PublishAlignShare(true);
|
|
|
time_edge_e = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2、pre detect",
|
|
|
"-------------------AI_Edge reuse from %s|%s--wait %d ms----succ---time %ld----\n",
|
|
|
strAlignSrcCamera.c_str(), strAlignSrcChannel.c_str(), nAlignReuseWaitMs, time_edge_e - time_edge_s);
|
|
|
// 定位模型不跑,但把手检测仍要执行:暗场图像 + 明场 bigmask
|
|
|
{
|
|
|
long handle_s = CheckUtil::getcurTime();
|
|
|
CheckHandleByBigMask(m_pEdge_Align_Result->bigmask);
|
|
|
long handle_e = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "handle detect use time %ld", handle_e - handle_s);
|
|
|
}
|
|
|
// 标签检测(独立步骤):暗场图像 + 明场 bigmask(不再沿用明场检出的标签);bDetTag 关闭时跳过
|
|
|
{
|
|
|
if (m_pFuntion->function.f_supportDet.bDetTag)
|
|
|
{
|
|
|
long tag_s = CheckUtil::getcurTime();
|
|
|
int reTag = CheckTagByBigMask(m_CheckResult_shareP->in_shareImage->img, m_pEdge_Align_Result->bigmask);
|
|
|
long tag_e = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Tag", "tag detect use time %ld", tag_e - tag_s);
|
|
|
if (reTag != 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "CheckTagByBigMask is error type = %d", reTag);
|
|
|
m_nErrorCode = reTag;
|
|
|
m_nCheckResultErrorCode = m_nErrorCode;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
m_tag_roiList.clear();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Tag", "bDetTag close, skip tag detect");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
reedge = AI_Edge(m_CheckResult_shareP->in_shareImage->img, m_outer_rroi, m_inner_rroi, m_tag_roiList);
|
|
|
if (reedge != 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "AI_Edge is error type = %d", reedge);
|
|
|
PublishAlignShare(false); // 定位失败:通知可能的等待方(暗场复用),让其回退自行定位
|
|
|
m_nErrorCode = reedge;
|
|
|
m_nCheckResultErrorCode = m_nErrorCode;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
PublishAlignShare(true);
|
|
|
time_edge_e = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2、pre detect", "-------------------------AI_Edge------------succ---time %ld----\n", time_edge_e - time_edge_s);
|
|
|
}
|
|
|
|
|
|
/*生成检测图:用 m_outer_rroi(旋转矩形)的外接区域直接从原图裁剪(不再旋转摆正,省掉旋转重采样耗时);
|
|
|
原图坐标 -> 检测图坐标 的映射为纯平移,存入 m_matSrc2Det / m_matDet2Src,后续坐标换算仍走这对矩阵*/
|
|
|
cv::Mat image = m_CheckResult_shareP->in_shareImage->img;
|
|
|
cv::Rect imgRect(0, 0, image.cols, image.rows);
|
|
|
|
|
|
// 1、检测图裁剪区域 = 旋转矩形外接区域(与原图求交,防越界)
|
|
|
cv::Rect padRoi = m_outer_rroi.boundingRect() & imgRect;
|
|
|
if (padRoi.width <= 0 || padRoi.height <= 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, "Error", "crop roi error [%d %d %d %d]", padRoi.x, padRoi.y, padRoi.width, padRoi.height);
|
|
|
m_nErrorCode = CHECK_ERROR_CheckImg_Empty;
|
|
|
m_nCheckResultErrorCode = m_nErrorCode;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
int detW = padRoi.width;
|
|
|
int detH = padRoi.height;
|
|
|
|
|
|
// 2、原图 -> 检测图 为纯平移(无旋转):检测图坐标 = 原图坐标 - 裁剪原点
|
|
|
m_matSrc2Det = (cv::Mat_<double>(2, 3) << 1, 0, -(double)padRoi.x, 0, 1, -(double)padRoi.y);
|
|
|
m_matDet2Src = (cv::Mat_<double>(2, 3) << 1, 0, (double)padRoi.x, 0, 1, (double)padRoi.y);
|
|
|
|
|
|
// 3、检测图:单通道灰度(彩色原图按 BGR 顺序转灰度,与 imread(path,0) 的亮度一致),直接裁剪;
|
|
|
// 彩色原图时再输出一张同样裁剪的彩色检测图,供裁剪图(_RE_CutImg)保存时保留原图通道
|
|
|
// (检测算法仍只用灰度 detImg)
|
|
|
m_deskewPadRoi = padRoi;
|
|
|
cv::Mat colorPatch = image(padRoi);
|
|
|
if (colorPatch.channels() == 1)
|
|
|
{
|
|
|
colorPatch.copyTo(m_pImageAllResult->detImg);
|
|
|
m_pImageAllResult->detColorImg.release();
|
|
|
}
|
|
|
else if (colorPatch.channels() == 3)
|
|
|
{
|
|
|
cv::cvtColor(colorPatch, m_pImageAllResult->detImg, cv::COLOR_BGR2GRAY);
|
|
|
colorPatch.copyTo(m_pImageAllResult->detColorImg);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cv::cvtColor(colorPatch, m_pImageAllResult->detImg, cv::COLOR_BGRA2GRAY);
|
|
|
colorPatch.copyTo(m_pImageAllResult->detColorImg);
|
|
|
}
|
|
|
|
|
|
// 4、检测图坐标系下 m_outer_roi 即整张检测图;原图坐标系下 CutRoi 仍取旋转矩形外接区域(兼容旧语义/JSON)
|
|
|
m_outer_roi = cv::Rect(0, 0, detW, detH);
|
|
|
m_Crop_Roi_paramImg = padRoi;
|
|
|
m_pImageAllResult->pDetResult->CutRoi = m_Crop_Roi_paramImg;
|
|
|
m_pImageAllResult->pDetResult->Param_CropRoi = m_Crop_Roi_paramImg;
|
|
|
// 检测图坐标 -> 原图坐标 的映射,供结果解析时取回缺陷小图/换算坐标
|
|
|
m_pImageAllResult->pDetResult->detToSrcMat = m_matDet2Src.clone();
|
|
|
|
|
|
// 5、参数(模板)坐标 -> 检测图坐标 的映射:
|
|
|
// 有对齐时先把参数点映射到原图再平移到检测图;无对齐时参数按原图坐标系处理,
|
|
|
// 直接按裁剪原点平移(与旧的裁剪平移完全一致)
|
|
|
{
|
|
|
if (m_pEdge_Align_Result && m_pEdge_Align_Result->buseOfft)
|
|
|
{
|
|
|
if (!m_pEdge_Align_Result->H.empty())
|
|
|
{
|
|
|
m_matParam2Det = MatAffineCompose(m_matSrc2Det, m_pEdge_Align_Result->H);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cv::Mat matOfft = (cv::Mat_<double>(2, 3) << 1, 0, (double)m_pEdge_Align_Result->offt_x, 0, 1, (double)m_pEdge_Align_Result->offt_y);
|
|
|
m_matParam2Det = MatAffineCompose(m_matSrc2Det, matOfft);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
m_matParam2Det = m_matSrc2Det.clone();
|
|
|
}
|
|
|
}
|
|
|
long time_calan_e = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "-------------------------crop--------------succ--- det [%d %d] src bbox [%d %d %d %d] time %ld----\n",
|
|
|
detW, detH, m_Crop_Roi_paramImg.x, m_Crop_Roi_paramImg.y, m_Crop_Roi_paramImg.width, m_Crop_Roi_paramImg.height,
|
|
|
time_calan_e - time_edge_s);
|
|
|
/*多线程开启 AI 推理检测*/
|
|
|
long time_AI_s = CheckUtil::getcurTime();
|
|
|
m_AItask = std::make_shared<TaskInfo>();
|
|
|
m_AItask->taskname = Task_AI;
|
|
|
m_task.sendTask(m_AItask);
|
|
|
|
|
|
ImgPreDet();
|
|
|
/*更新检测区域*/
|
|
|
Update_DetRoiList();
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "3、pre detect", "-------------------------pre Det--------------- \n");
|
|
|
|
|
|
/* 检测支架偏移(bDetSupport 开关关闭时不做支架判定)
|
|
|
不使用参数绘制区域,结合两次定位的 mask 计算:
|
|
|
1、bigmask 最大连通域 = 产品区域 rp(近似矩形,四周可能有凸起)
|
|
|
2、smallmask 最大连通域 = 支架区域 rs(按定位裁剪区 m_outer_rroi 外接矩形映射回原图坐标)
|
|
|
3、按 edge_select 指定方向(left/right/up/down)计算 rs 中心到 rp 对应边缘的距离
|
|
|
4、计算值与标准值(x_standard/y_standard)做差取平均,平均差超过 offset(x_offset) → 偏移 */
|
|
|
{
|
|
|
Function_Support_Det &supportDet = m_pFuntion->function.f_supportDet;
|
|
|
if (supportDet.bOpen && supportDet.bDetSupport)
|
|
|
{
|
|
|
const cv::Mat &bigmask = m_pEdge_Align_Result->bigmask;
|
|
|
const cv::Mat &smallmask = m_pEdge_Align_Result->smallmask;
|
|
|
if (bigmask.empty() || smallmask.empty())
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "mask empty (big %d small %d), skip support offset", bigmask.empty(), smallmask.empty());
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// 1、mask 二值化(兼容 0/1 与 0/255 掩膜)
|
|
|
cv::Mat bigBin, smallBin;
|
|
|
{
|
|
|
cv::Mat grayMask;
|
|
|
if (bigmask.channels() != 1)
|
|
|
{
|
|
|
cv::cvtColor(bigmask, grayMask, cv::COLOR_BGR2GRAY);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
grayMask = bigmask;
|
|
|
}
|
|
|
double dMin = 0;
|
|
|
double dMax = 0;
|
|
|
cv::minMaxLoc(grayMask, &dMin, &dMax);
|
|
|
cv::threshold(grayMask, bigBin, dMax > 1 ? dMax * 0.5 : 0, 255, cv::THRESH_BINARY);
|
|
|
}
|
|
|
{
|
|
|
cv::Mat grayMask;
|
|
|
if (smallmask.channels() != 1)
|
|
|
{
|
|
|
cv::cvtColor(smallmask, grayMask, cv::COLOR_BGR2GRAY);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
grayMask = smallmask;
|
|
|
}
|
|
|
double dMin = 0;
|
|
|
double dMax = 0;
|
|
|
cv::minMaxLoc(grayMask, &dMin, &dMax);
|
|
|
cv::threshold(grayMask, smallBin, dMax > 1 ? dMax * 0.5 : 0, 255, cv::THRESH_BINARY);
|
|
|
}
|
|
|
|
|
|
// 2、最大连通域轮廓:bigmask -> 产品区域 rp(原图坐标);smallmask -> 支架区域 rs(smallmask 局部坐标)
|
|
|
bool bFoundRp = false;
|
|
|
std::vector<cv::Point> rpContour = CheckUtil::getLargestContourROI(bigBin, bFoundRp);
|
|
|
bool bFoundRs = false;
|
|
|
std::vector<cv::Point> rsContour = CheckUtil::getLargestContourROI(smallBin, bFoundRs);
|
|
|
if (!bFoundRp || rpContour.size() < 3 || !bFoundRs || rsContour.size() < 3)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测", "contour invalid (rp %d %ld, rs %d %ld), skip support offset",
|
|
|
bFoundRp, rpContour.size(), bFoundRs, rsContour.size());
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// rp 边缘:产品最大连通域的外接矩形(含凸起)
|
|
|
cv::Rect rpRect = cv::boundingRect(rpContour);
|
|
|
|
|
|
// rs 映射回原图坐标:smallmask 对应定位时裁剪的大轮廓区域(m_outer_rroi 外接矩形),按比例映射
|
|
|
cv::Rect bigRect = m_outer_rroi.boundingRect();
|
|
|
float fx = bigRect.width * 1.0f / smallmask.cols;
|
|
|
float fy = bigRect.height * 1.0f / smallmask.rows;
|
|
|
cv::Rect rsSmallRect = cv::boundingRect(rsContour);
|
|
|
cv::Rect rsRect(
|
|
|
cvRound(bigRect.x + rsSmallRect.x * fx),
|
|
|
cvRound(bigRect.y + rsSmallRect.y * fy),
|
|
|
cvRound(rsSmallRect.width * fx),
|
|
|
cvRound(rsSmallRect.height * fy));
|
|
|
|
|
|
// 3、rs 中心到 rp 上下左右边缘的距离
|
|
|
float fCx = rsRect.x + rsRect.width * 0.5f;
|
|
|
float fCy = rsRect.y + rsRect.height * 0.5f;
|
|
|
float fDL = fCx - rpRect.x; // 到左边缘
|
|
|
float fDR = (rpRect.x + rpRect.width) - fCx; // 到右边缘
|
|
|
float fDT = fCy - rpRect.y; // 到上边缘
|
|
|
float fDB = (rpRect.y + rpRect.height) - fCy; // 到下边缘
|
|
|
|
|
|
// 4、按 edge_select 指定方向(left/right/up/down)取计算值,与标准值做差取平均,平均差超过 offset → 偏移
|
|
|
// left/right 用 x_standard,up/down 用 y_standard;offset 用 x_offset
|
|
|
float fSumDiff = 0;
|
|
|
int nDiffNum = 0;
|
|
|
std::string strSelect = "";
|
|
|
std::string strDiff = "";
|
|
|
char strTmp[64];
|
|
|
for (size_t iselect = 0; iselect < supportDet.edge_select.size(); iselect++)
|
|
|
{
|
|
|
const std::string &strEdge = supportDet.edge_select[iselect];
|
|
|
float fDist = 0;
|
|
|
float fStandard = 0;
|
|
|
if (strEdge == "left")
|
|
|
{
|
|
|
fDist = fDL;
|
|
|
fStandard = supportDet.x_standard;
|
|
|
}
|
|
|
else if (strEdge == "right")
|
|
|
{
|
|
|
fDist = fDR;
|
|
|
fStandard = supportDet.x_standard;
|
|
|
}
|
|
|
else if (strEdge == "up")
|
|
|
{
|
|
|
fDist = fDT;
|
|
|
fStandard = supportDet.y_standard;
|
|
|
}
|
|
|
else if (strEdge == "down")
|
|
|
{
|
|
|
fDist = fDB;
|
|
|
fStandard = supportDet.y_standard;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
float fDiff = fabs(fDist - fStandard);
|
|
|
fSumDiff += fDiff;
|
|
|
nDiffNum++;
|
|
|
sprintf(strTmp, "%s(%.1f/%.1f) ", strEdge.c_str(), fDist, fStandard);
|
|
|
strSelect += strTmp;
|
|
|
sprintf(strTmp, "%.1f ", fDiff);
|
|
|
strDiff += strTmp;
|
|
|
}
|
|
|
// edge_select 为空或无效时 nDiffNum=0,结果不参与判定(仅打日志)
|
|
|
float fAvgDiff = (nDiffNum > 0) ? (fSumDiff / nDiffNum) : 0.0f;
|
|
|
bool bOffset = (nDiffNum > 0) && (fAvgDiff > supportDet.x_offset);
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "支架检测",
|
|
|
"rp [%d %d %d %d] rs [%d %d %d %d] scale (%.4f %.4f) center (%.1f %.1f) dL %.1f dR %.1f dT %.1f dB %.1f | select %s diff %s avgDiff %.1f limit %.1f ng %d",
|
|
|
rpRect.x, rpRect.y, rpRect.width, rpRect.height,
|
|
|
rsRect.x, rsRect.y, rsRect.width, rsRect.height,
|
|
|
fx, fy, fCx, fCy, fDL, fDR, fDT, fDB,
|
|
|
strSelect.c_str(), strDiff.c_str(), fAvgDiff, supportDet.x_offset, bOffset);
|
|
|
|
|
|
if (bOffset)
|
|
|
{
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
|
|
|
// roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系
|
|
|
cv::Rect roi = MatAffineRect(m_matSrc2Det, rsRect);
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
temerror.roi = roi;
|
|
|
temerror.area = roi.width * roi.height;
|
|
|
temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y;
|
|
|
temerror.JudgArea_second = temerror.JudgArea;
|
|
|
float w = rsRect.width;
|
|
|
float h = rsRect.height;
|
|
|
temerror.flen = (w > h ? w : h) * m_fImgage_Scale_X;
|
|
|
temerror.fbreadth = (w > h ? h : w) * m_fImgage_Scale_Y;
|
|
|
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_support_offset;
|
|
|
temerror.qx_name = CONFIG_QX_NAME_Names[temerror.nconfig_qx_type];
|
|
|
temerror.result = QX_RESULT_TYPE_NG;
|
|
|
temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG];
|
|
|
|
|
|
// 计算支架中心所在的检测区域
|
|
|
cv::Point pCenter;
|
|
|
pCenter.x = roi.x + roi.width * 0.5;
|
|
|
pCenter.y = roi.y + roi.height * 0.5;
|
|
|
int nmaxregionIdx = 0;
|
|
|
for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++)
|
|
|
{
|
|
|
const std::vector<cv::Point> &polygon = m_DetRoiList.roiList_Src[iregion];
|
|
|
double result = cv::pointPolygonTest(polygon, pCenter, false);
|
|
|
if (result < 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
nmaxregionIdx = iregion;
|
|
|
}
|
|
|
temerror.detRegionidxList.push_back(nmaxregionIdx);
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* 把手缺失检测:bigmask 的凸起本身不算 NG,只有"参数里的某个把手区域附近找不到把手"才算 NG(每个缺失区域报一条) */
|
|
|
{
|
|
|
if (m_bHandleDetSucc && m_bHandleExpect && !m_bHandleFound)
|
|
|
{
|
|
|
for (size_t iRegion = 0; iRegion < m_HandleRegionResultList.size(); iRegion++)
|
|
|
{
|
|
|
Handle_Region_Result_ ®ionResult = m_HandleRegionResultList[iRegion];
|
|
|
if (regionResult.bFound)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// 上报该把手区域的位置:参数(模板)坐标 -> 检测图(detImg)坐标
|
|
|
cv::Rect roi = MatAffineRect(m_matParam2Det, regionResult.boxRegion);
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
if (roi.width <= 0 || roi.height <= 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
temerror.roi = roi;
|
|
|
temerror.area = roi.width * roi.height;
|
|
|
temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y;
|
|
|
temerror.JudgArea_second = temerror.JudgArea;
|
|
|
float w = roi.width;
|
|
|
float h = roi.height;
|
|
|
temerror.flen = (w > h ? w : h) * m_fImgage_Scale_X;
|
|
|
temerror.fbreadth = (w > h ? h : w) * m_fImgage_Scale_Y;
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_handle_loss;
|
|
|
temerror.qx_name = CONFIG_QX_NAME_Names[CONFIG_QX_NAME_handle_loss];
|
|
|
temerror.result = QX_RESULT_TYPE_NG;
|
|
|
temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG];
|
|
|
|
|
|
// 计算把手区域中心所在的检测区域
|
|
|
cv::Point pCenter;
|
|
|
pCenter.x = roi.x + roi.width * 0.5;
|
|
|
pCenter.y = roi.y + roi.height * 0.5;
|
|
|
int nmaxregionIdx = 0;
|
|
|
for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++)
|
|
|
{
|
|
|
const std::vector<cv::Point> &polygon = m_DetRoiList.roiList_Src[iregion];
|
|
|
double result = cv::pointPolygonTest(polygon, pCenter, false);
|
|
|
if (result < 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
nmaxregionIdx = iregion;
|
|
|
}
|
|
|
temerror.detRegionidxList.push_back(nmaxregionIdx);
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "把手检测", "%s %d region %d roi [%d %d %d %d]",
|
|
|
temerror.qx_name.c_str(), temerror.Idx, regionResult.nIdx,
|
|
|
roi.x, roi.y, roi.width, roi.height);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Tag检测(bDetTag 关闭时不上报标签 NG) */
|
|
|
{
|
|
|
if (!m_pFuntion->function.f_supportDet.bDetTag)
|
|
|
{
|
|
|
m_tag_roiList.clear();
|
|
|
}
|
|
|
for (const auto &tagRoi : m_tag_roiList)
|
|
|
{
|
|
|
// tag roi 在原图坐标系,按坐标映射转换到检测图(detImg)坐标系
|
|
|
cv::Rect roi = MatAffineRect(m_matSrc2Det, tagRoi.boundingRect());
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
if (roi.width <= 0 || roi.height <= 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
temerror.roi = roi;
|
|
|
temerror.area = roi.width * roi.height;
|
|
|
temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y;
|
|
|
temerror.JudgArea_second = temerror.JudgArea;
|
|
|
float tw = tagRoi.size.width;
|
|
|
float th = tagRoi.size.height;
|
|
|
temerror.flen = (tw > th ? tw : th) * m_fImgage_Scale_X;
|
|
|
temerror.fbreadth = (tw > th ? th : tw) * m_fImgage_Scale_Y;
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_cell_tag;
|
|
|
temerror.qx_name = CONFIG_QX_NAME_Names[CONFIG_QX_NAME_cell_tag];
|
|
|
temerror.result = QX_RESULT_TYPE_NG;
|
|
|
temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG];
|
|
|
|
|
|
// 计算标签中心所在的检测区域
|
|
|
cv::Point pCenter;
|
|
|
pCenter.x = roi.x + roi.width * 0.5;
|
|
|
pCenter.y = roi.y + roi.height * 0.5;
|
|
|
int nmaxregionIdx = 0;
|
|
|
for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++)
|
|
|
{
|
|
|
const std::vector<cv::Point> &polygon = m_DetRoiList.roiList_Src[iregion];
|
|
|
double result = cv::pointPolygonTest(polygon, pCenter, false);
|
|
|
if (result < 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
nmaxregionIdx = iregion;
|
|
|
}
|
|
|
temerror.detRegionidxList.push_back(nmaxregionIdx);
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Tag检测", "tag %d roi [%d %d %d %d] -> NG", temerror.Idx, roi.x, roi.y, roi.width, roi.height);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
m_CheckResult_shareP->nresult = 0;
|
|
|
|
|
|
/*把临时可以绘制的结果都绘制出来。*/
|
|
|
DrawResult_Step_1();
|
|
|
long time_AI_e;
|
|
|
/*AI 推理生成*/
|
|
|
{
|
|
|
long t211 = CheckUtil::getcurTime();
|
|
|
// 对AI的结果进行 处理,等待 AI 推理全部完成。
|
|
|
rec = AIMaskDet();
|
|
|
if (rec != CHECK_OK)
|
|
|
{
|
|
|
m_nErrorCode = rec;
|
|
|
m_nCheckResultErrorCode = m_nErrorCode;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
time_AI_e = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "4、detect", "-------------------------2、AI Detect--------%ld wait AI complate %ld-------\n", time_AI_e - time_AI_s, time_AI_e - t211);
|
|
|
|
|
|
m_CheckResult_shareP->resultMaskImg = m_pImageAllResult->AIMaskImg;
|
|
|
}
|
|
|
|
|
|
{
|
|
|
|
|
|
t4 = CheckUtil::getcurTime();
|
|
|
rec = GetCheckResultBLob();
|
|
|
if (rec != CHECK_OK)
|
|
|
{
|
|
|
m_nErrorCode = rec;
|
|
|
m_nCheckResultErrorCode = m_nErrorCode;
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
t5 = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "5、BLob ", "------------------------ --------%ld -------\n", t5 - t4);
|
|
|
}
|
|
|
// m_pdetlog->printLog(m_strCurDetChannel);
|
|
|
|
|
|
long te = CheckUtil::getcurTime();
|
|
|
m_pdetlog->bPrintStr = true;
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, "result", " ALL use Time %ld edge %ld AI %ld blob %ld",
|
|
|
te - t1, time_edge_e - time_edge_s, time_AI_e - time_AI_s, t5 - t4);
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, "End", " Check Run");
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::SetNewConfig()
|
|
|
{
|
|
|
if (m_nConfigIdx < 0)
|
|
|
{
|
|
|
return 1;
|
|
|
/* code */
|
|
|
}
|
|
|
|
|
|
if (m_pConfig->GetConfigUpdataStatus(ConfigType_Analysisy_Common_XL, m_nConfigIdx))
|
|
|
{
|
|
|
m_bupdateconfig = true;
|
|
|
// printf("************** ImgCheckAnalysisy::SetNewConfig m_nConfigIdx %d\n", m_nConfigIdx);
|
|
|
m_old_productROI = cv::Rect(0, 0, 0, 0);
|
|
|
m_pConfig->GetConfig(ConfigType_Analysisy_Common_XL, &m_AnalysisyConfig);
|
|
|
if (m_AnalysisyConfig.commonCheckConfig.nodeConfigArr.size() > 0)
|
|
|
{
|
|
|
m_pCommonAnalysisyConfig = &m_AnalysisyConfig.commonCheckConfig.nodeConfigArr.at(0);
|
|
|
m_pBasicConfig = &m_AnalysisyConfig.commonCheckConfig.baseConfig;
|
|
|
m_pRegionAnalysisyParam = &m_pCommonAnalysisyConfig->regionConfigArr.at(0);
|
|
|
|
|
|
UpdateImgageScale();
|
|
|
if (false)
|
|
|
{
|
|
|
printf("SetNewConfig m_nConfigIdx %d m_CheckConfig.strSkuName %s \n", m_nConfigIdx, m_AnalysisyConfig.strSkuName.c_str());
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
printf("m_AnalysisyConfig.commonCheckConfig.nodeConfigArr == 0 \n");
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// printf("ConfigType_Analysisy_Common_XL no Update \n");
|
|
|
}
|
|
|
m_nErrorCode = CHECK_OK;
|
|
|
return CHECK_OK;
|
|
|
}
|
|
|
|
|
|
ChannelCheckFunction *ImgCheckAnalysisy::GetChannelFuntion(std::string strChannelName)
|
|
|
{
|
|
|
ChannelCheckFunction *p = NULL;
|
|
|
for (int i = 0; i < m_AnalysisyConfig.checkFunction.channelFunctionArr.size(); i++)
|
|
|
{
|
|
|
if (CheckUtil::compareIgnoreCase(m_AnalysisyConfig.checkFunction.channelFunctionArr[i].strChannelName, strChannelName))
|
|
|
{
|
|
|
p = &m_AnalysisyConfig.checkFunction.channelFunctionArr[i];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return p;
|
|
|
}
|
|
|
int ImgCheckAnalysisy::Run(int nId)
|
|
|
{
|
|
|
std::vector<int> vi;
|
|
|
vi.push_back(nId);
|
|
|
auto nRet = set_cpu_id(vi);
|
|
|
// printf("Check So %d bind cpu ret %d, %d\n", m_nThreadIdx, nRet, nId);
|
|
|
while (!m_bExit)
|
|
|
{
|
|
|
// 数据准备完成,开启检测
|
|
|
if (m_nRun_Status == CHECK_THREAD_STATUS_READY)
|
|
|
{
|
|
|
m_nRun_Status = CHECK_THREAD_STATUS_BUSY;
|
|
|
/* 检测 */
|
|
|
CheckRun();
|
|
|
m_nRun_Status = CHECK_THREAD_STATUS_COMPLETE;
|
|
|
m_pImageAllResult->setStep(ImageAllResult::DetStep_Complet);
|
|
|
m_nRun_Status = CHECK_THREAD_STATUS_IDLE;
|
|
|
// printf("*--------%d\n", m_nRun_Status);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
|
|
|
usleep(1000);
|
|
|
}
|
|
|
// printf("*-");
|
|
|
usleep(1000);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
int ImgCheckAnalysisy::set_cpu_id(const std::vector<int> &cpu_set_vec)
|
|
|
{
|
|
|
// for cpu affinity
|
|
|
int nRet = 0;
|
|
|
#ifdef __linux
|
|
|
cpu_set_t _cur_cpu_set;
|
|
|
CPU_ZERO(&_cur_cpu_set);
|
|
|
for (auto _id : cpu_set_vec)
|
|
|
{
|
|
|
CPU_SET(_id, &_cur_cpu_set);
|
|
|
}
|
|
|
if (0 > pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &_cur_cpu_set))
|
|
|
{
|
|
|
perror("set cpu affinity failed: ");
|
|
|
printf("Warning: set cpu affinity failed ... ...\n");
|
|
|
nRet = -1;
|
|
|
}
|
|
|
#endif //__linux
|
|
|
return nRet;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::CalBlob_Other()
|
|
|
{
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "CalBlob_Other", " Start");
|
|
|
|
|
|
float fs_x = m_fImgage_Scale_X;
|
|
|
float fs_y = m_fImgage_Scale_Y;
|
|
|
|
|
|
float fs_resize_x = m_pImageAllResult->fscale_detToresult_x;
|
|
|
float fs_resize_y = m_pImageAllResult->fscale_detToresult_y;
|
|
|
// 遍历每个检测blob
|
|
|
for (int i = 0; i < blobs.blobCount; i++)
|
|
|
{
|
|
|
|
|
|
// m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Blob", "%d/%d start", i, blobs.blobCount);
|
|
|
|
|
|
cv::Rect roi;
|
|
|
roi.x = blobs.blobTab[i].minx;
|
|
|
roi.y = blobs.blobTab[i].miny;
|
|
|
roi.width = blobs.blobTab[i].maxx - blobs.blobTab[i].minx + 1;
|
|
|
roi.height = blobs.blobTab[i].maxy - blobs.blobTab[i].miny + 1;
|
|
|
|
|
|
cv::Scalar result = calc_blob_info_withstats(m_pImageAllResult->detImg, m_pImageAllResult->AIMaskImg, roi);
|
|
|
|
|
|
int QX_whiteBLACK = CONFIG_QX_BLACK;
|
|
|
if (result[0] > 0)
|
|
|
{
|
|
|
QX_whiteBLACK = CONFIG_QX_WHITE;
|
|
|
}
|
|
|
|
|
|
double hj = result[2];
|
|
|
double energe = result[1];
|
|
|
blobs.blobTab[i].energy = energe;
|
|
|
blobs.blobTab[i].grayDis = hj;
|
|
|
|
|
|
float JudgArea = blobs.blobTab[i].area * fs_x * fs_y;
|
|
|
blobs.blobTab[i].JudgArea = JudgArea;
|
|
|
float flen = roi.width * fs_x;
|
|
|
float fwid = roi.height * fs_x;
|
|
|
if (roi.height * fs_y > flen)
|
|
|
{
|
|
|
flen = roi.height * fs_y;
|
|
|
fwid = roi.width * fs_y;
|
|
|
}
|
|
|
blobs.blobTab[i].len = flen;
|
|
|
blobs.blobTab[i].breadth = fwid;
|
|
|
|
|
|
int nerrortype = 0;
|
|
|
int checkFlage = 0;
|
|
|
|
|
|
float fmaxScore = 0;
|
|
|
int config_qx_type = 0;
|
|
|
|
|
|
if (blobs.blobTab[i].ErrType == ERR_TYPE_2)
|
|
|
{
|
|
|
QX_whiteBLACK = CONFIG_QX_BLACK; // 强制为 黑色
|
|
|
}
|
|
|
blobs.blobTab[i].whiteOrblack = QX_whiteBLACK;
|
|
|
|
|
|
// 精确计算长度
|
|
|
vector<float> re_len = Cal_QXLen(m_pImageAllResult->detImg(roi), config_qx_type, fs_x, fs_y);
|
|
|
|
|
|
if (re_len.size() > 2)
|
|
|
{
|
|
|
if(re_len[0] >= 0){
|
|
|
flen = re_len[0];
|
|
|
blobs.blobTab[i].len = flen;
|
|
|
}
|
|
|
if(re_len[1] >= 0){
|
|
|
fwid = re_len[1];
|
|
|
blobs.blobTab[i].breadth = fwid;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::GetClassImg(const cv::Mat &img, cv::Mat &AIdetImg, cv::Rect qx_roi, int detwidth, int detheight)
|
|
|
{
|
|
|
|
|
|
cv::Rect cutroi;
|
|
|
cv::Rect roi = qx_roi;
|
|
|
bool bresize = false;
|
|
|
{
|
|
|
|
|
|
int pc_x = roi.x + roi.width * 0.5;
|
|
|
int pc_y = roi.y + roi.height * 0.5;
|
|
|
|
|
|
if (roi.width < detwidth && roi.height < detheight)
|
|
|
{
|
|
|
cutroi.width = detwidth;
|
|
|
cutroi.x = pc_x - detwidth * 0.5;
|
|
|
cutroi.height = detheight;
|
|
|
cutroi.y = pc_y - detheight * 0.5;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// 宽 高
|
|
|
if (roi.width > roi.height)
|
|
|
{
|
|
|
cutroi.width = roi.width + 20;
|
|
|
cutroi.x = roi.x - 10;
|
|
|
|
|
|
float fsx = detheight * 1.0f / detwidth;
|
|
|
cutroi.height = cutroi.width * fsx;
|
|
|
cutroi.y = pc_y - cutroi.height * 0.5;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cutroi.height = roi.height + 20;
|
|
|
cutroi.y = roi.y - 10;
|
|
|
|
|
|
float fsy = detwidth * 1.0f / detheight;
|
|
|
cutroi.width = cutroi.height * fsy;
|
|
|
cutroi.x = pc_x - cutroi.width * 0.5;
|
|
|
}
|
|
|
|
|
|
bresize = true;
|
|
|
}
|
|
|
|
|
|
if (cutroi.x < 0)
|
|
|
{
|
|
|
cutroi.x = 0;
|
|
|
}
|
|
|
if (cutroi.y < 0)
|
|
|
{
|
|
|
cutroi.y = 0;
|
|
|
}
|
|
|
if (cutroi.x + cutroi.width >= img.cols)
|
|
|
{
|
|
|
cutroi.x = img.cols - cutroi.width;
|
|
|
if (cutroi.x < 0)
|
|
|
{
|
|
|
cutroi.x = 0;
|
|
|
if (cutroi.x + cutroi.width >= img.cols)
|
|
|
{
|
|
|
cutroi.width = img.cols;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (cutroi.y + cutroi.height >= img.rows)
|
|
|
{
|
|
|
cutroi.y = img.rows - cutroi.height;
|
|
|
if (cutroi.y < 0)
|
|
|
{
|
|
|
cutroi.y = 0;
|
|
|
if (cutroi.y + cutroi.height >= img.rows)
|
|
|
{
|
|
|
cutroi.height = img.rows;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (!CheckUtil::RoiInImg(cutroi, img))
|
|
|
{
|
|
|
return 1;
|
|
|
}
|
|
|
cv::Size sz;
|
|
|
sz.width = detwidth;
|
|
|
sz.height = detheight;
|
|
|
if (cutroi.width != sz.width || cutroi.height != sz.height)
|
|
|
{
|
|
|
cv::resize(img(cutroi), AIdetImg, sz);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
AIdetImg = img(cutroi).clone();
|
|
|
}
|
|
|
|
|
|
if (1 != AIdetImg.channels())
|
|
|
{
|
|
|
cv::cvtColor(AIdetImg, AIdetImg, cv::COLOR_BGR2GRAY);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
vector<float> ImgCheckAnalysisy::Cal_QXLen(cv::Mat qx_maskImg, int qx_type, float fsc_x, float fsc_y)
|
|
|
{
|
|
|
vector<float> resLen(3);
|
|
|
|
|
|
float nlen = -1;
|
|
|
cv::Mat detimg = qx_maskImg;
|
|
|
|
|
|
resLen[0] = nlen;
|
|
|
// 寻找轮廓
|
|
|
vector<vector<Point>> contours;
|
|
|
cv::findContours(detimg, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
// 找到最大面积的轮廓
|
|
|
double maxArea = -1;
|
|
|
int maxAreaIdx = -1;
|
|
|
for (size_t i = 0; i < contours.size(); ++i)
|
|
|
{
|
|
|
double area = contourArea(contours[i]);
|
|
|
if (area > maxArea)
|
|
|
{
|
|
|
maxArea = area;
|
|
|
maxAreaIdx = i;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 如果找到了最大面积的轮廓
|
|
|
if (maxAreaIdx >= 0)
|
|
|
{
|
|
|
|
|
|
// 使用minAreaRect找到最小外接矩形
|
|
|
cv::RotatedRect rect = cv::minAreaRect(contours[maxAreaIdx]);
|
|
|
|
|
|
// // 绘制最小外接矩形
|
|
|
// Point2f vertices[4];
|
|
|
// rect.points(vertices);
|
|
|
// for (int i = 0; i < 4; ++i)
|
|
|
// {
|
|
|
// line(detimg, vertices[i], vertices[(i + 1) % 4], Scalar(128, 255, 0), 2); // 绿色线
|
|
|
// }
|
|
|
// 输出结果
|
|
|
|
|
|
// 获取最小外接矩形的尺寸
|
|
|
float width = rect.size.width;
|
|
|
float height = rect.size.height;
|
|
|
|
|
|
// std::cout << "1 Width: " << width << ", Height: " << height << std::endl;
|
|
|
Point2f vertices[4];
|
|
|
rect.points(vertices);
|
|
|
|
|
|
vector<Point2f> newcont;
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
{
|
|
|
Point2f p;
|
|
|
p.x = vertices[i].x * fsc_x;
|
|
|
p.y = vertices[i].y * fsc_y;
|
|
|
newcont.push_back(p);
|
|
|
}
|
|
|
vector<vector<Point2f>> contours_New;
|
|
|
contours_New.push_back(newcont);
|
|
|
// Recreate the rotated rectangle with scaled vertices
|
|
|
RotatedRect scaledRect = minAreaRect(contours_New[0]);
|
|
|
|
|
|
// Calculate scaled width and height
|
|
|
width = scaledRect.size.width;
|
|
|
height = scaledRect.size.height;
|
|
|
// std::cout << "2 width: " << width << ", height: " << height << std::endl;
|
|
|
// float scale = 0.5; // 缩放比例
|
|
|
// rect.size.width *= fsc_x;
|
|
|
// rect.size.height *= fsc_y;
|
|
|
// std::cout << "fsc_x: " << fsc_x << ", fsc_y: " << fsc_y << std::endl;
|
|
|
// std::cout << "Width: " << width << ", Height: " << height << std::endl;
|
|
|
// 获取最小外接矩形的尺寸
|
|
|
// width = rect.size.width;
|
|
|
// height = rect.size.height;
|
|
|
if (width > height)
|
|
|
{
|
|
|
nlen = width;
|
|
|
resLen[0] = width;
|
|
|
resLen[1] = height;
|
|
|
/* code */
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
nlen = height;
|
|
|
resLen[0] = height;
|
|
|
resLen[1] = width;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// getchar();
|
|
|
return resLen;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::GetCheckResultBLob()
|
|
|
{
|
|
|
long t1, t2, t3, t4, t5, t6, t7;
|
|
|
t1 = CheckUtil::getcurTime();
|
|
|
int re = GetALLBlob();
|
|
|
if (re != 0)
|
|
|
{
|
|
|
return re;
|
|
|
}
|
|
|
|
|
|
// 缺陷分类 多线程 实现。
|
|
|
long t11 = CheckUtil::getcurTime();
|
|
|
m_Classtask = std::make_shared<TaskInfo>();
|
|
|
m_Classtask->taskname = Task_Class;
|
|
|
m_task.sendTask(m_Classtask);
|
|
|
|
|
|
re = CalBlob_Other();
|
|
|
if (re != 0)
|
|
|
{
|
|
|
return re;
|
|
|
}
|
|
|
long t12 = CheckUtil::getcurTime();
|
|
|
// 等待分类 完成。
|
|
|
m_Classtask->waitComplate();
|
|
|
long t13 = CheckUtil::getcurTime();
|
|
|
// printf("=========== cls time %ld waite time %ld\n", t13 - t11, t13 - t12);
|
|
|
|
|
|
BLobToDetResult();
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void worker(const unsigned char *img, int w, int startRow, int endRow, unsigned char *rowFlags)
|
|
|
{
|
|
|
for (int y = startRow; y < endRow; y++)
|
|
|
{
|
|
|
const unsigned char *p = img + (size_t)y * w;
|
|
|
rowFlags[y] = 0;
|
|
|
for (int x = 0; x < w; x++)
|
|
|
{
|
|
|
if (p[x] != 0)
|
|
|
{
|
|
|
rowFlags[y] = 1;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
int ImgCheckAnalysisy::GetALLBlob()
|
|
|
{
|
|
|
|
|
|
std::string strBaseLog = "Blob";
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "image Blob Analysis Start");
|
|
|
if (m_pImageAllResult->AIMaskImg.empty())
|
|
|
{
|
|
|
return 1;
|
|
|
}
|
|
|
unsigned char *pGrayErrordata = (unsigned char *)m_pImageAllResult->AIMaskImg.data;
|
|
|
int width = m_pImageAllResult->AIMaskImg.cols;
|
|
|
int height = m_pImageAllResult->AIMaskImg.rows;
|
|
|
|
|
|
long t1 = CheckUtil::getcurTime();
|
|
|
|
|
|
memset(&blobs, 0x00, sizeof(ERROR_DOTS_BLOBS));
|
|
|
|
|
|
ERROR_DOTS_BLOBS blobs_v1;
|
|
|
memset(&blobs_v1, 0x00, sizeof(ERROR_DOTS_BLOBS));
|
|
|
ERROR_DOTS_BLOBS blobs_big;
|
|
|
memset(&blobs_big, 0x00, sizeof(ERROR_DOTS_BLOBS));
|
|
|
ERROR_DOTS_BLOBS blobs_big_2;
|
|
|
memset(&blobs_big_2, 0x00, sizeof(ERROR_DOTS_BLOBS));
|
|
|
printf("=====>>>> GetALLBlob m_strCurDetChannel %s \n", m_strCurDetChannel.c_str());
|
|
|
if (m_strCurDetChannel == "CA")
|
|
|
{
|
|
|
printf("=====>>>>GetALLBlob USE CA %s \n", m_strCurDetChannel.c_str());
|
|
|
GetBlobs_ALL_New(&blobs_v1, pGrayErrordata, m_ImgBlobHFlagData, width, height, 15);
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "blob 1 num %d", blobs_v1.blobCount);
|
|
|
|
|
|
if (blobs_v1.srcBlobCount >= _MAX_ERROR_DOT_BLOB)
|
|
|
{
|
|
|
|
|
|
GetBlobs_ALL_New(&blobs_big, pGrayErrordata, m_ImgBlobHFlagData, width, height, 200);
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "blob 2 num %d", blobs_big.blobCount);
|
|
|
|
|
|
if (blobs_big.srcBlobCount >= _MAX_ERROR_DOT_BLOB)
|
|
|
{
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "blob 3 num %d", blobs_big_2.blobCount);
|
|
|
GetBlobs_ALL_New(&blobs_big_2, pGrayErrordata, m_ImgBlobHFlagData, width, height, 500);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
GetBlobs_oneLabe(&blobs_v1, pGrayErrordata, m_ImgBlobHFlagData, width, height, 15);
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "blob 1 num %d", blobs_v1.blobCount);
|
|
|
|
|
|
if (blobs_v1.srcBlobCount >= _MAX_ERROR_DOT_BLOB)
|
|
|
{
|
|
|
|
|
|
GetBlobs_oneLabe(&blobs_big, pGrayErrordata, m_ImgBlobHFlagData, width, height, 200);
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "blob 2 num %d", blobs_big.blobCount);
|
|
|
|
|
|
if (blobs_big.srcBlobCount >= _MAX_ERROR_DOT_BLOB)
|
|
|
{
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "blob 3 num %d", blobs_big_2.blobCount);
|
|
|
GetBlobs_oneLabe(&blobs_big_2, pGrayErrordata, m_ImgBlobHFlagData, width, height, 500);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
long t2 = CheckUtil::getcurTime();
|
|
|
PushBlob(&blobs, &blobs_big_2);
|
|
|
PushBlob(&blobs, &blobs_big);
|
|
|
PushBlob(&blobs, &blobs_v1);
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "blob ALL num %d", blobs.blobCount);
|
|
|
// long t2 = CheckUtil::getcurTime();
|
|
|
// printf(" BLob time %ld \n", t2 - t1);
|
|
|
|
|
|
if (false || DetImgInfo_shareP->bsaveProcessImg)
|
|
|
{
|
|
|
int font_face = cv::FONT_HERSHEY_SIMPLEX;
|
|
|
double font_scale = 0.5;
|
|
|
int thickness = 1;
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strBaseLog, "Save Tem Img");
|
|
|
cv::Mat tm = m_pImageAllResult->AIMaskImg.clone(); // 保持 mask 单通道,不再灰度转假彩
|
|
|
for (int i = 0; i < blobs.blobCount; i++)
|
|
|
{
|
|
|
cv::Rect roi;
|
|
|
roi.x = blobs.blobTab[i].minx;
|
|
|
roi.y = blobs.blobTab[i].miny;
|
|
|
roi.width = blobs.blobTab[i].maxx - blobs.blobTab[i].minx + 1;
|
|
|
roi.height = blobs.blobTab[i].maxy - blobs.blobTab[i].miny + 1;
|
|
|
|
|
|
if (blobs.blobTab[i].ErrType == 0)
|
|
|
{
|
|
|
cv::rectangle(tm, roi, CheckUtil::GetDrawColor(tm, cv::Scalar(0, 0, 255)));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cv::rectangle(tm, roi, CheckUtil::GetDrawColor(tm, cv::Scalar(0, 255, 255)));
|
|
|
}
|
|
|
|
|
|
char buffer[128];
|
|
|
sprintf(buffer, "id:%d type:%d m %0.1f", i,blobs.blobTab[i].ErrType, blobs.blobTab[i].density);
|
|
|
std::string text = buffer;
|
|
|
cv::Point origin = cv::Point(roi.x, roi.y);
|
|
|
cv::putText(tm, text, origin, font_face, font_scale, CheckUtil::GetDrawColor(tm, cv::Scalar(0, 255, 0)), thickness, 1, 0);
|
|
|
|
|
|
// printf("type: %d %d %d %d %d %d %d\n", blobs.blobTab[i].ErrType, blobs.blobTab[i].area, blobs.blobTab[i].energy, roi.x, roi.y, roi.width, roi.height);
|
|
|
}
|
|
|
cv::imwrite(m_CheckResult_shareP->in_shareImage->strChannel + "_image_resize_blob.png", tm);
|
|
|
}
|
|
|
|
|
|
long te = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "image Blob Analysis End use time %ld blob %ld ms", te - t1, t2 - t1);
|
|
|
|
|
|
// getchar();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::AIMaskDet()
|
|
|
{
|
|
|
|
|
|
// 计算 mask 图片的一行 是否有 残点,用以加速 Blob的计算。
|
|
|
if (m_ImgBlobHFlagData)
|
|
|
{
|
|
|
delete[] m_ImgBlobHFlagData;
|
|
|
m_ImgBlobHFlagData = NULL;
|
|
|
}
|
|
|
m_ImgBlobHFlagData = new unsigned char[m_pImageAllResult->detImg.rows];
|
|
|
memset(m_ImgBlobHFlagData, 0, sizeof(unsigned char) * m_pImageAllResult->detImg.rows);
|
|
|
|
|
|
while (true)
|
|
|
{
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
std::shared_ptr<AIMulThreadRunBase::AITask> AImaskResult;
|
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(mtx_AIMaskImgBLobQueue);
|
|
|
if (m_AIMaskImgBLobQueue.size() > 0)
|
|
|
{
|
|
|
AImaskResult = std::move(m_AIMaskImgBLobQueue.front());
|
|
|
m_AIMaskImgBLobQueue.pop();
|
|
|
// printf("size ============== %ld\n", m_AIMaskImgBLobQueue.size());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 有AI mask 的结果
|
|
|
if (AImaskResult)
|
|
|
{
|
|
|
// static int ss = 0;
|
|
|
cv::Mat &outimg = *(AImaskResult->output);
|
|
|
// cv::imwrite(std::to_string(ss++) + ".png", outimg);
|
|
|
|
|
|
unsigned char *pGrayErrordata = (unsigned char *)outimg.data;
|
|
|
int width = outimg.cols;
|
|
|
int height = outimg.rows;
|
|
|
int start_Y = AImaskResult->roi.y;
|
|
|
if (cv::countNonZero(outimg != 0) != 0)
|
|
|
{
|
|
|
// #pragma omp parallel for
|
|
|
for (int y = 0; y < height; y++)
|
|
|
{
|
|
|
if (m_ImgBlobHFlagData[y + start_Y] == 0)
|
|
|
{
|
|
|
unsigned char *p = pGrayErrordata + y * width;
|
|
|
for (int x = 0; x < width; x++)
|
|
|
{
|
|
|
if (p[x] != 0)
|
|
|
{
|
|
|
m_ImgBlobHFlagData[y + start_Y] = 1;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/* code */
|
|
|
|
|
|
if (m_AItask->isComplate())
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// printf("===========================================1 \n");
|
|
|
m_AItask->waitComplate();
|
|
|
int rec = m_AItask->nresult;
|
|
|
if (rec != CHECK_OK)
|
|
|
{
|
|
|
return rec;
|
|
|
}
|
|
|
// printf("===========================================2 \n");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::Contours()
|
|
|
{
|
|
|
// std::string strBaseLog = "Contours";
|
|
|
// // m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "Contours Start");
|
|
|
// cv::Mat detimg;
|
|
|
// {
|
|
|
// // 膨胀
|
|
|
// cv::Mat se = getStructuringElement(0, Size(5, 5)); // 构造矩形结构元素
|
|
|
// cv::dilate(m_pdetlog->temImgList[TEM_IMG_IDX_AImask], detimg, se);
|
|
|
// }
|
|
|
|
|
|
// std::vector<std::vector<cv::Point>> contours;
|
|
|
// std::vector<cv::Vec4i> hierarchy;
|
|
|
// cv::findContours(detimg, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); // 只找最外层轮廓
|
|
|
|
|
|
// // cv::cvtColor(m_pdetlog->temImgList[TEM_IMG_IDX_SrcCrop], m_pdetlog->temImgList[TEM_IMG_IDX_DrawSrc], cv::COLOR_GRAY2BGR);
|
|
|
|
|
|
// for (int i = 0; i < contours.size(); ++i)
|
|
|
// { // 绘制所有轮廓
|
|
|
// cv::drawContours(m_pdetlog->temImgList[TEM_IMG_IDX_DrawSrc], contours, i, cv::Scalar(255, 0, 255)); // thickness为-1时为填充整个轮廓
|
|
|
// }
|
|
|
|
|
|
// float fx = m_pdetlog->temImgList[TEM_IMG_IDX_Result].cols * 1.0f / m_pdetlog->temImgList[TEM_IMG_IDX_SrcCrop].cols;
|
|
|
// float fy = m_pdetlog->temImgList[TEM_IMG_IDX_Result].rows * 1.0f / m_pdetlog->temImgList[TEM_IMG_IDX_SrcCrop].rows;
|
|
|
// for (int i = 0; i < contours.size(); ++i)
|
|
|
// {
|
|
|
// for (int j = 0; j < contours.at(i).size(); ++j)
|
|
|
// {
|
|
|
// contours.at(i).at(j).x *= fx;
|
|
|
// contours.at(i).at(j).y *= fy;
|
|
|
// }
|
|
|
// }
|
|
|
// // cv::cvtColor(m_pdetlog->temImgList[TEM_IMG_IDX_Result], m_pdetlog->temImgList[TEM_IMG_IDX_Result], cv::COLOR_GRAY2BGR);
|
|
|
|
|
|
// for (int i = 0; i < contours.size(); ++i)
|
|
|
// { // 绘制所有轮廓
|
|
|
// cv::drawContours(m_pdetlog->temImgList[TEM_IMG_IDX_Result], contours, i, cv::Scalar(255, 0, 255)); // thickness为-1时为填充整个轮廓
|
|
|
// }
|
|
|
// cv::imwrite("TEM_IMG_IDX_Result.png", m_pdetlog->temImgList[TEM_IMG_IDX_Result]);
|
|
|
// cv::imwrite("deeeee.png", m_pdetlog->temImgList[TEM_IMG_IDX_Drawmask]);
|
|
|
// getchar();
|
|
|
// m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "Contours End");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::CheckImgInit()
|
|
|
{
|
|
|
// 1、初始化
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList = std::make_shared<std::vector<QX_ERROR_INFO_>>();
|
|
|
m_pdetlog->Init();
|
|
|
m_pdetlog->addLogLevel = DET_LOG_LEVEL_3;
|
|
|
memset(&blobs, 0, sizeof(ERROR_DOTS_BLOBS));
|
|
|
m_nCheckResultErrorCode = 0;
|
|
|
|
|
|
m_Draw_qxImageResult.erase(m_Draw_qxImageResult.begin(), m_Draw_qxImageResult.end());
|
|
|
|
|
|
m_DetRoiList.Init();
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::ConfigCheck(cv::Mat img)
|
|
|
{
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "start", "Config Check");
|
|
|
int re = CHECK_OK;
|
|
|
if (img.empty())
|
|
|
{
|
|
|
m_nErrorCode = CHECK_ERROR_CheckImg_Empty;
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, "Error", "check Img empty");
|
|
|
return m_nErrorCode;
|
|
|
}
|
|
|
|
|
|
// if (m_pCommonAnalysisyConfig->regionConfigArr.size() <= 0)
|
|
|
// {
|
|
|
// m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Error", "regionConfig Num = 0");
|
|
|
// m_nErrorCode = CHECK_ERROR_Config_Value;
|
|
|
// return m_nErrorCode;
|
|
|
// }
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Succ", "Config Check Succ");
|
|
|
return re;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg)
|
|
|
{
|
|
|
std::shared_ptr<AIModel_Base> pAIDet;
|
|
|
// printf("=====>>>>AI_Detect_Thread m_strCurDetChannel %s \n", m_strCurDetChannel.c_str());
|
|
|
{
|
|
|
// printf("=====>>>>AI_Detect_Thread USE CA %s \n", m_strCurDetChannel.c_str());
|
|
|
pAIDet = AI_Factory->Defect;
|
|
|
}
|
|
|
|
|
|
|
|
|
std::string strBaseLog = "AI_Detect";
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "AI_Detect Start");
|
|
|
long t1, t2, t3;
|
|
|
t1 = CheckUtil::getcurTime();
|
|
|
|
|
|
SmallRoiList.clear();
|
|
|
SmallRoiList.erase(SmallRoiList.begin(), SmallRoiList.end());
|
|
|
cv::Rect cutRoi;
|
|
|
cutRoi.x = 0;
|
|
|
cutRoi.y = 0;
|
|
|
cutRoi.width = img.cols - 0;
|
|
|
cutRoi.height = img.rows - 0;
|
|
|
|
|
|
int deal_image_width = pAIDet->input_0.width;
|
|
|
int deal_image_height = pAIDet->input_0.height;
|
|
|
|
|
|
int re = CheckUtil::cutSmallImg(img, SmallRoiList, cutRoi, deal_image_width, deal_image_height, 0, 0);
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strBaseLog, " cutSmallImg Num %d", SmallRoiList.size());
|
|
|
if (re != 0 || SmallRoiList.size() <= 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strBaseLog, " cutSmallImg error %d", re);
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "AI_Detect End");
|
|
|
return re;
|
|
|
/* code */
|
|
|
}
|
|
|
|
|
|
// 临时存图
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
{
|
|
|
|
|
|
cv::Mat imshow = img.clone(); // 保持输入通道,不再灰度转假彩
|
|
|
for (int i = 0; i < SmallRoiList.size(); i++)
|
|
|
{
|
|
|
cv::rectangle(imshow, SmallRoiList.at(i), CheckUtil::GetDrawColor(imshow, cv::Scalar(0, 0, 255)), 3);
|
|
|
}
|
|
|
cv::imwrite(DetImgInfo_shareP->strChannel + "_AI_Det_ROI.jpg", imshow);
|
|
|
}
|
|
|
|
|
|
ResultImg = cv::Mat::zeros(img.size(), CV_8UC1);
|
|
|
|
|
|
const int totalTasks = SmallRoiList.size();
|
|
|
int submitted = 0;
|
|
|
int completed = 0;
|
|
|
|
|
|
// std::string str_Root = m_strRootPath + pDetConfig->strProductID + "_" + pDetConfig->strchannel + "_";
|
|
|
t2 = CheckUtil::getcurTime();
|
|
|
while (completed < totalTasks)
|
|
|
{
|
|
|
// 如果任务还没提交完,且当前处理任务数 < 2,提交新任务
|
|
|
if (submitted < totalTasks && runner->GetProcessingCount() < 10)
|
|
|
{
|
|
|
|
|
|
std::shared_ptr<AIMulThreadRunBase::AITask> task = std::make_shared<AIMulThreadRunBase::AITask>();
|
|
|
task->id = completed;
|
|
|
task->roi = SmallRoiList.at(submitted);
|
|
|
task->input = img(SmallRoiList.at(submitted)).clone();
|
|
|
task->output = std::make_shared<cv::Mat>();
|
|
|
task->engine = pAIDet;
|
|
|
|
|
|
runner->SubmitTask(task);
|
|
|
submitted++;
|
|
|
}
|
|
|
|
|
|
// 尝试取结果
|
|
|
std::shared_ptr<AIMulThreadRunBase::AITask> result;
|
|
|
if (runner->PopResult(result))
|
|
|
{
|
|
|
|
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(mtx_AIMaskImgBLobQueue);
|
|
|
m_AIMaskImgBLobQueue.push(result);
|
|
|
}
|
|
|
|
|
|
cv::Mat &outimg = *(result->output);
|
|
|
if (!outimg.empty())
|
|
|
{
|
|
|
outimg.copyTo(ResultImg(result->roi), outimg);
|
|
|
}
|
|
|
{
|
|
|
std::shared_ptr<ImageAllResult::AI_Det_MaskImg> temAIresult = std::make_shared<ImageAllResult::AI_Det_MaskImg>();
|
|
|
temAIresult->roi = result->roi;
|
|
|
temAIresult->AI_inImg = result->input;
|
|
|
temAIresult->AI_mask = outimg;
|
|
|
m_pImageAllResult->AI_Qx_MaskList.push_back(temAIresult);
|
|
|
}
|
|
|
completed++;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
t3 = CheckUtil::getcurTime();
|
|
|
float mean_AI = (t3 - t2) / SmallRoiList.size();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strBaseLog, " AI Run Time: sum %ld pre %ld Run %ld mean One Small Img %f", t3 - t1, t2 - t1, t3 - t2, mean_AI);
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, strBaseLog, "AI_Detect End");
|
|
|
|
|
|
// 临时存图
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
{
|
|
|
cv::imwrite(DetImgInfo_shareP->strChannel + "_AI_mask.png", ResultImg);
|
|
|
}
|
|
|
|
|
|
// getchar();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::AI_QX_Class_Thread()
|
|
|
{
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "QX_Class", " Start");
|
|
|
|
|
|
std::shared_ptr<AIModel_Base> pAIDet;
|
|
|
// printf("=====>>>>AI_QX_Class_Thread m_strCurDetChannel %s \n", m_strCurDetChannel.c_str());
|
|
|
{
|
|
|
// printf("=====>>>>AI_QX_Class_Thread USE CA %s \n", m_strCurDetChannel.c_str());
|
|
|
pAIDet = AI_Factory->Class;
|
|
|
}
|
|
|
|
|
|
int deal_image_width = pAIDet->input_0.width;
|
|
|
int deal_image_height = pAIDet->input_0.height;
|
|
|
|
|
|
const int totalTasks = blobs.blobCount;
|
|
|
int submitted = 0;
|
|
|
int completed = 0;
|
|
|
int detblobIdx = 0;
|
|
|
int clsnum = 0;
|
|
|
// std::string str_Root = m_strRootPath + pDetConfig->strProductID + "_" + pDetConfig->strchannel + "_";
|
|
|
long t2 = CheckUtil::getcurTime();
|
|
|
while (completed < totalTasks)
|
|
|
{
|
|
|
// std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
if (submitted < totalTasks)
|
|
|
{
|
|
|
ERROR_DOTS_BLOB_DATA *pblob = &blobs.blobTab[submitted];
|
|
|
if (pblob->ErrType == ERR_TYPE_2)
|
|
|
{
|
|
|
pblob->AIclasstype = CONFIG_QX_NAME_cell_ymhs;
|
|
|
submitted++;
|
|
|
completed++;
|
|
|
continue;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// 如果任务还没提交完,且当前处理任务数 < 2,提交新任务
|
|
|
if (runner->GetProcessingCount() < 10)
|
|
|
{
|
|
|
clsnum++;
|
|
|
cv::Rect roi;
|
|
|
roi.x = pblob->minx;
|
|
|
roi.y = pblob->miny;
|
|
|
roi.width = pblob->maxx - pblob->minx + 1;
|
|
|
roi.height = pblob->maxy - pblob->miny + 1;
|
|
|
|
|
|
std::shared_ptr<AIMulThreadRunBase::AITask> task = std::make_shared<AIMulThreadRunBase::AITask>();
|
|
|
task->id = submitted;
|
|
|
int re = GetClassImg(m_pImageAllResult->detImg, task->input, roi, deal_image_width, deal_image_height);
|
|
|
if (re == 0)
|
|
|
{
|
|
|
task->bclass = true;
|
|
|
task->engine = pAIDet;
|
|
|
runner->SubmitTask(task);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
completed++;
|
|
|
}
|
|
|
|
|
|
submitted++;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 尝试取结果
|
|
|
std::shared_ptr<AIMulThreadRunBase::AITask> result;
|
|
|
if (runner->PopResult(result))
|
|
|
{
|
|
|
|
|
|
int temClass = result->cls_label;
|
|
|
int cls_num = 0;
|
|
|
|
|
|
switch (temClass)
|
|
|
{
|
|
|
case 0:
|
|
|
cls_num = AI_CLass_QX_NAME_aotudian;
|
|
|
break;
|
|
|
|
|
|
case 1:
|
|
|
cls_num = AI_CLass_QX_NAME_other;
|
|
|
break;
|
|
|
|
|
|
case 2:
|
|
|
cls_num = AI_CLass_QX_NAME_line;
|
|
|
break;
|
|
|
|
|
|
case 3:
|
|
|
cls_num = AI_CLass_QX_NAME_zangwu;
|
|
|
break;
|
|
|
|
|
|
case 4:
|
|
|
cls_num = AI_CLass_QX_NAME_dianzhuang;
|
|
|
break;
|
|
|
|
|
|
case 5:
|
|
|
cls_num = AI_CLass_QX_NAME_posun;
|
|
|
break;
|
|
|
|
|
|
case 6:
|
|
|
cls_num = AI_CLass_QX_NAME_xianwei;
|
|
|
break;
|
|
|
|
|
|
case 7:
|
|
|
cls_num = AI_CLass_QX_NAME_shuizi;
|
|
|
break;
|
|
|
|
|
|
case 8:
|
|
|
cls_num = AI_CLass_QX_NAME_danban;
|
|
|
break;
|
|
|
|
|
|
case 9:
|
|
|
cls_num = AI_CLass_QX_NAME_fuchen;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
cls_num = AI_CLass_QX_NAME_zangwu;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// std::string strclassName = AI_CLass_QX_NAME_Names[cls_num];
|
|
|
// printf("AI Class num %d = %s %f\n", cls_num, strclassName.c_str(), result->cls_score);
|
|
|
|
|
|
// 分类存图 开启。
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveAlginImg)
|
|
|
{
|
|
|
|
|
|
std::string saveimgpaht = "";
|
|
|
|
|
|
if (m_strCurDetChannel == "TA")
|
|
|
{
|
|
|
saveimgpaht = m_strRootPath_TA_cls + std::to_string(cls_num) + "/" + std::to_string(CheckUtil::getcurTime()) + "_" + std::to_string(result->cls_score) + ".png";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
saveimgpaht = m_strRootPath_CA_cls + std::to_string(cls_num) + "/" + std::to_string(CheckUtil::getcurTime()) + "_" + std::to_string(result->cls_score) + ".png";
|
|
|
}
|
|
|
|
|
|
{
|
|
|
m_pImageStorage->addImage(saveimgpaht, result->input);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int configqx = AIClassTypeToConfigType(cls_num);
|
|
|
|
|
|
ERROR_DOTS_BLOB_DATA *pblob = &blobs.blobTab[result->id];
|
|
|
pblob->AIclasstype = configqx;
|
|
|
|
|
|
completed++;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
}
|
|
|
}
|
|
|
// printf("=====>>>>AI_QX_Class_Thread clsnum %d \n", clsnum);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::ThreadTask(int nId)
|
|
|
{
|
|
|
|
|
|
while (!m_bExit)
|
|
|
{
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
|
// 等待是否有任务
|
|
|
std::shared_ptr<TaskInfo> task = m_task.GetTask();
|
|
|
|
|
|
// 把任务发送给对应的任务处理函数进行处理
|
|
|
switch (task->taskname)
|
|
|
{
|
|
|
case Task_AI:
|
|
|
TaskFun_AIDet(task);
|
|
|
break;
|
|
|
case Task_Class:
|
|
|
TaskFun_QxClass(task);
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::ResizeImg()
|
|
|
{
|
|
|
|
|
|
cv::Size sz;
|
|
|
sz.width = RESIZE_IMAGE_WIDTH;
|
|
|
|
|
|
float fw = RESIZE_IMAGE_WIDTH * 1.0f / m_pImageAllResult->detImg.cols;
|
|
|
sz.height = int(m_pImageAllResult->detImg.rows * fw);
|
|
|
|
|
|
// 结果图画布取自原图裁剪区(平移映射),保持原图通道(彩色原图 -> 真实彩色,不再灰度转假彩);
|
|
|
// 异常情况(如未走裁剪流程)直接用检测图兜底
|
|
|
const cv::Mat &srcImg = m_CheckResult_shareP->in_shareImage->img;
|
|
|
if (m_CheckResult_shareP->in_shareImage->Det_Mode != DET_MODE_ReJson &&
|
|
|
!srcImg.empty() &&
|
|
|
m_deskewPadRoi.width > 0 && m_deskewPadRoi.height > 0 &&
|
|
|
CheckUtil::RoiInImg(m_deskewPadRoi, srcImg))
|
|
|
{
|
|
|
cv::resize(srcImg(m_deskewPadRoi), m_pImageAllResult->resultImg, sz);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cv::resize(m_pImageAllResult->detImg, m_pImageAllResult->resultImg, sz);
|
|
|
}
|
|
|
m_CheckResult_shareP->resultimg = m_pImageAllResult->resultImg;
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, "showImg size", " [w %d,h %d]-> show img [w %d,h %d] ",
|
|
|
m_outer_roi.width, m_outer_roi.height, sz.width, sz.height);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void ImgCheckAnalysisy::TaskFun_AIDet(std::shared_ptr<TaskInfo> task)
|
|
|
{
|
|
|
task->SetStatus(TaskStep_run);
|
|
|
long t1, t2;
|
|
|
// AI 推理生成
|
|
|
{
|
|
|
t1 = CheckUtil::getcurTime();
|
|
|
|
|
|
int rec = AI_Detect_Thread(m_pImageAllResult->detImg, m_pImageAllResult->AIMaskImg);
|
|
|
|
|
|
t2 = CheckUtil::getcurTime();
|
|
|
task->nresult = rec;
|
|
|
}
|
|
|
task->SetStatus(TaskStep_compate);
|
|
|
}
|
|
|
|
|
|
void ImgCheckAnalysisy::TaskFun_QxClass(std::shared_ptr<TaskInfo> task)
|
|
|
{
|
|
|
task->SetStatus(TaskStep_run);
|
|
|
long t1, t2;
|
|
|
// AI 推理生成
|
|
|
{
|
|
|
t1 = CheckUtil::getcurTime();
|
|
|
|
|
|
int rec = AI_QX_Class_Thread();
|
|
|
|
|
|
t2 = CheckUtil::getcurTime();
|
|
|
task->nresult = rec;
|
|
|
}
|
|
|
task->SetStatus(TaskStep_compate);
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::Update_DetRoiList()
|
|
|
{
|
|
|
float fx = m_pImageAllResult->fscale_detToresult_x;
|
|
|
float fy = m_pImageAllResult->fscale_detToresult_y;
|
|
|
|
|
|
// 参数多边形 -> 检测图(裁剪坐标系) 的映射在裁剪时已算好(m_matParam2Det)
|
|
|
for (const auto ®ion : m_pCommonAnalysisyConfig->regionConfigArr)
|
|
|
{
|
|
|
m_DetRoiList.Update_Mat(region.basicInfo.pointArry, m_matParam2Det, fx, fy);
|
|
|
}
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "info", "roiList_Src roi num %ld", m_DetRoiList.roiList_Src.size());
|
|
|
// m_DetRoiList.print("m_DetRoiList");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::DrawResult_Step_1()
|
|
|
{
|
|
|
|
|
|
// std::cout << m_pBasicConfig->bDrawPreRoi << std::endl;
|
|
|
if (m_pBasicConfig->bDrawPreRoi)
|
|
|
{
|
|
|
int n = 0;
|
|
|
// std::cout << m_DetRoiList.roiList_Show.size() << std::endl;
|
|
|
for (const auto &polygon : m_DetRoiList.roiList_Show)
|
|
|
{
|
|
|
// n++;
|
|
|
// if (n == 1)
|
|
|
// {
|
|
|
// continue;
|
|
|
// }
|
|
|
|
|
|
// std::cout << polygon << std::endl;
|
|
|
|
|
|
// 绘制多边形的边界(不填充),线宽为1;颜色按画布通道自适应
|
|
|
cv::polylines(m_CheckResult_shareP->resultimg, polygon, true,
|
|
|
CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, cv::Scalar(128, 128, 255)), 1); // true表示闭合多边形
|
|
|
}
|
|
|
// cv::imwrite(DetImgInfo_shareP->strChannel + "roi_src.png", m_pdetlog->temImgList[TEM_IMG_IDX_DrawSrc]);
|
|
|
// cv::imwrite(DetImgInfo_shareP->strChannel + "roi_ss.png", m_pdetlog->temImgList[TEM_IMG_IDX_Result]);
|
|
|
|
|
|
// getchar();
|
|
|
}
|
|
|
|
|
|
float fs_resize_x = m_pImageAllResult->resultImg.cols * 1.0f / m_pImageAllResult->detImg.cols;
|
|
|
float fs_resize_y = m_pImageAllResult->resultImg.rows * 1.0f / m_pImageAllResult->detImg.rows;
|
|
|
|
|
|
|
|
|
// 绘制m_inner_rroi(蓝色),原图坐标 -> 检测图(裁剪) -> 结果图
|
|
|
{
|
|
|
cv::Point2f vertices[4];
|
|
|
m_inner_rroi.points(vertices);
|
|
|
std::vector<cv::Point> draw_pts;
|
|
|
draw_pts.reserve(4);
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
{
|
|
|
cv::Point2f detPt = MatAffinePoint(m_matSrc2Det, vertices[i]);
|
|
|
draw_pts.emplace_back(cvRound(detPt.x * fs_resize_x),
|
|
|
cvRound(detPt.y * fs_resize_y));
|
|
|
}
|
|
|
cv::polylines(m_CheckResult_shareP->resultimg, draw_pts, true,
|
|
|
CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, cv::Scalar(255, 0, 0)), 1);
|
|
|
}
|
|
|
|
|
|
if (m_pbaseCheckFunction && m_pbaseCheckFunction->markLine.region.size() > 1)
|
|
|
{
|
|
|
std::vector<cv::Point> product_roi_show;
|
|
|
for (const auto &pt : m_pbaseCheckFunction->markLine.region)
|
|
|
{
|
|
|
// 参数(模板)坐标 -> 检测图(裁剪) -> 结果图
|
|
|
cv::Point2f detPt = MatAffinePoint(m_matParam2Det, cv::Point2f((float)pt.x, (float)pt.y));
|
|
|
cv::Point draw_pt;
|
|
|
draw_pt.x = cvRound(detPt.x * fs_resize_x);
|
|
|
draw_pt.y = cvRound(detPt.y * fs_resize_y);
|
|
|
product_roi_show.push_back(draw_pt);
|
|
|
}
|
|
|
|
|
|
cv::polylines(
|
|
|
m_CheckResult_shareP->resultimg,
|
|
|
product_roi_show,
|
|
|
true,
|
|
|
CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, cv::Scalar(0, 255, 0)),
|
|
|
1);
|
|
|
}
|
|
|
if (m_pEdge_Align_Result.get() != NULL)
|
|
|
{
|
|
|
|
|
|
if (m_pbaseCheckFunction->markLine.bDraw)
|
|
|
{
|
|
|
for (size_t i = 0; i < m_pEdge_Align_Result->markresulList.size(); i++)
|
|
|
{
|
|
|
// if (m_pEdge_Align_Result->markresulList[i].status == Mark_Result_Status_OK)
|
|
|
{
|
|
|
const cv::Point &markPt = m_pEdge_Align_Result->markresulList[i].det_Local_DetImg;
|
|
|
cv::Point2f detPt = MatAffinePoint(m_matSrc2Det, cv::Point2f((float)markPt.x, (float)markPt.y));
|
|
|
cv::Point p;
|
|
|
p.x = cvRound(detPt.x * fs_resize_x);
|
|
|
p.y = cvRound(detPt.y * fs_resize_y);
|
|
|
cv::circle(m_CheckResult_shareP->resultimg, p, 5,
|
|
|
CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, cv::Scalar(0, 255, 0)));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// cv::imwrite("sss.png", m_CheckResult_shareP->resultimg);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::AIClassTypeToConfigType(int nAIQXType)
|
|
|
{
|
|
|
|
|
|
int resultError_type = CONFIG_QX_NAME_cell_aotudian;
|
|
|
switch (nAIQXType)
|
|
|
{
|
|
|
case AI_CLass_QX_NAME_aotudian:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_aotudian;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_other:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_other;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_line:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_line;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_zangwu:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_zangwu;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_dianzhuang:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_dianzhuang;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_posun:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_posun;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_xianwei:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_xianwei;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_shuizi:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_shuizi;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_danban:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_danban;
|
|
|
break;
|
|
|
case AI_CLass_QX_NAME_fuchen:
|
|
|
resultError_type = CONFIG_QX_NAME_cell_fuchen;
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
return resultError_type;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::UpdateImgageScale()
|
|
|
{
|
|
|
|
|
|
if (m_pBasicConfig->fImage_Scale_x > 0 && m_pBasicConfig->fImage_Scale_x < 1 &&
|
|
|
m_pBasicConfig->fImage_Scale_y > 0 && m_pBasicConfig->fImage_Scale_y < 1)
|
|
|
{
|
|
|
|
|
|
m_fImgage_Scale_X = m_pBasicConfig->fImage_Scale_x;
|
|
|
m_fImgage_Scale_Y = m_pBasicConfig->fImage_Scale_y;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
int ImgCheckAnalysisy::BLobToDetResult()
|
|
|
{
|
|
|
long t1 = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "BLobToDetResult", " Start old qx num %ld", m_pDetResult->pQx_ErrorList->size());
|
|
|
|
|
|
// 遍历每个检测blob
|
|
|
for (int i = 0; i < blobs.blobCount; i++)
|
|
|
{
|
|
|
cv::Rect roi;
|
|
|
roi.x = blobs.blobTab[i].minx;
|
|
|
roi.y = blobs.blobTab[i].miny;
|
|
|
roi.width = blobs.blobTab[i].maxx - blobs.blobTab[i].minx + 1;
|
|
|
roi.height = blobs.blobTab[i].maxy - blobs.blobTab[i].miny + 1;
|
|
|
|
|
|
int config_qx_type = blobs.blobTab[i].AIclasstype;
|
|
|
|
|
|
float JudgArea = blobs.blobTab[i].JudgArea;
|
|
|
float fsecondArea = JudgArea;
|
|
|
std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type];
|
|
|
|
|
|
if (true)
|
|
|
{
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
temerror.roi = roi;
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
temerror.area = blobs.blobTab[i].area;
|
|
|
temerror.JudgArea = JudgArea;
|
|
|
temerror.JudgArea_second = fsecondArea;
|
|
|
temerror.energy = blobs.blobTab[i].energy;
|
|
|
temerror.flen = blobs.blobTab[i].len;
|
|
|
temerror.fbreadth = blobs.blobTab[i].breadth;
|
|
|
temerror.nconfig_qx_type = config_qx_type;
|
|
|
temerror.qx_name = qx_name;
|
|
|
temerror.maxValue = blobs.blobTab[i].maxValue;
|
|
|
temerror.grayDis = blobs.blobTab[i].grayDis;
|
|
|
temerror.density = blobs.blobTab[i].density;
|
|
|
temerror.fUpIou = 0;
|
|
|
temerror.whiteOrBlack = blobs.blobTab[i].whiteOrblack;
|
|
|
|
|
|
{
|
|
|
cv::Point pCenter;
|
|
|
pCenter.x = roi.x + roi.width * 0.5;
|
|
|
pCenter.y = roi.y + roi.height * 0.5;
|
|
|
int nmaxregionIdx = 0;
|
|
|
for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++)
|
|
|
{
|
|
|
const std::vector<cv::Point> &polygon = m_DetRoiList.roiList_Src[iregion];
|
|
|
double result = cv::pointPolygonTest(polygon, pCenter, false);
|
|
|
if (result < 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
nmaxregionIdx = iregion;
|
|
|
}
|
|
|
|
|
|
temerror.detRegionidxList.push_back(nmaxregionIdx);
|
|
|
}
|
|
|
|
|
|
// {
|
|
|
// cv::Point pCenter;
|
|
|
// pCenter.x = roi.x + roi.width * 0.5;
|
|
|
// pCenter.y = roi.y + roi.height * 0.5;
|
|
|
// for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++)
|
|
|
// {
|
|
|
// const std::vector<cv::Point> &polygon = m_DetRoiList.roiList_Src[iregion];
|
|
|
// double result = cv::pointPolygonTest(polygon, pCenter, false);
|
|
|
// if (result < 0)
|
|
|
// {
|
|
|
// continue;
|
|
|
// }
|
|
|
// temerror.detRegionidxList.push_back(iregion);
|
|
|
// }
|
|
|
// if (temerror.detRegionidxList.size() <= 0)
|
|
|
// {
|
|
|
// temerror.detRegionidxList.push_back(0);
|
|
|
// }
|
|
|
// }
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
|
|
|
// printf("- %s idx %d a %f v %d h %f l %f\n", DetImgInfo_shareP->strChannel.c_str(), i, JudgArea, blobs.blobTab[i].maxValue, blobs.blobTab[i].grayDis, flen);
|
|
|
}
|
|
|
}
|
|
|
long t2 = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "BLobToDetResult", " end qx num %ld use time %ld", m_pDetResult->pQx_ErrorList->size(), t2 - t1);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::AI_Edge(const cv::Mat &img, cv::RotatedRect &outerRoi, cv::RotatedRect &innerRoi, vector<cv::RotatedRect> &tagroiList)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "AI_Edge", "-------------------start--------------");
|
|
|
|
|
|
AI_Edge_Algin::DetConfig config;
|
|
|
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
{
|
|
|
config.bSaveResultImg = true;
|
|
|
}
|
|
|
config.strChannel = DetImgInfo_shareP->strChannel;
|
|
|
config.pBaseCheckFunction = m_pbaseCheckFunction;
|
|
|
config.bDetTag = m_pFuntion->function.f_supportDet.bDetTag; // 标签检测开关:关闭时不跑 Tag_Loc
|
|
|
|
|
|
/* 定位获取三个roi */
|
|
|
int re = m_pAI_Edge_Algin.Detect(img, &config, m_pEdge_Align_Result);
|
|
|
if (re != 0)
|
|
|
{
|
|
|
printf("AI_Edge Is Error = %d \n", re);
|
|
|
}
|
|
|
if (!m_pEdge_Align_Result->bigroi.empty())
|
|
|
{
|
|
|
outerRoi = m_pEdge_Align_Result->bigroi[0];
|
|
|
}
|
|
|
if (!m_pEdge_Align_Result->smallroi.empty())
|
|
|
{
|
|
|
innerRoi = m_pEdge_Align_Result->smallroi[0];
|
|
|
}
|
|
|
// 无条件赋值:Tag_Loc 未检出或 bDetTag 关闭(tagroi 为空)时清空,避免残留上一张图的标签
|
|
|
tagroiList = m_pEdge_Align_Result->tagroi;
|
|
|
|
|
|
/*使用m_pEdge_Align_Result->bigmask检测把手是否缺失*/
|
|
|
{
|
|
|
long handle_s = CheckUtil::getcurTime();
|
|
|
CheckHandleByBigMask(m_pEdge_Align_Result->bigmask);
|
|
|
long handle_e = CheckUtil::getcurTime();
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "handle detect use time %ld", handle_e - handle_s);
|
|
|
}
|
|
|
return re;
|
|
|
}
|
|
|
|
|
|
// 用 bigmask 检测把手:
|
|
|
// 1、bigmask 主体近似矩形,把手是主体四周凸起的"类矩形块"
|
|
|
// 2、把主体四周的凸起抠出来(凸起在该边的边界值上表现为一次突变进入 + 一次反向突变离开)
|
|
|
// 3、参数里每个非空的"把手区域"附近都应该有一个把手:在区域附近找凸起,
|
|
|
// 凸起宽高与区域外接矩形宽高相近(HANDLE_SIZE_MIN_RATIO ~ HANDLE_SIZE_MAX_RATIO)才算匹配
|
|
|
// 4、凸起本身不算 NG,只有"某个把手区域附近找不到匹配的凸起"才算把手缺失(NG 由 CheckRun 上报)
|
|
|
int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &bigmask)
|
|
|
{
|
|
|
m_bHandleDetSucc = false;
|
|
|
m_bHandleFound = false;
|
|
|
m_bHandleExpect = false;
|
|
|
m_HandleCandList.clear();
|
|
|
m_HandleRegionResultList.clear();
|
|
|
|
|
|
if (bigmask.empty())
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "bigmask is empty, skip handle detect");
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
// 1、抠出 bigmask 四周凸起的矩形块
|
|
|
std::vector<Mask_Protrusion_> protrusionList;
|
|
|
int re = CheckUtil::GetMaskProtrusions(bigmask, protrusionList);
|
|
|
if (re != 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "GetMaskProtrusions error %d", re);
|
|
|
return re;
|
|
|
}
|
|
|
m_bHandleDetSucc = true;
|
|
|
|
|
|
// 2、把手检测开关 + 把手区域(最多4个,非空区域表示该处应该有一个把手)
|
|
|
bool bDetHandle = false;
|
|
|
std::vector<cv::Rect> regionBoxList;
|
|
|
if (m_pFuntion != NULL)
|
|
|
{
|
|
|
const Function_Support_Det &supportDet = m_pFuntion->function.f_supportDet;
|
|
|
bDetHandle = supportDet.bDetHandle;
|
|
|
for (size_t i = 0; i < supportDet.handleBoxes.size() && i < HANDLE_REGION_MAX_NUM; i++)
|
|
|
{
|
|
|
regionBoxList.push_back(supportDet.handleBoxes[i]);
|
|
|
}
|
|
|
}
|
|
|
m_bHandleExpect = (bDetHandle && !regionBoxList.empty());
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "protrusion num %ld handle region num %ld bDetHandle %s expect %s",
|
|
|
protrusionList.size(), regionBoxList.size(), BOOL_TO_STR(bDetHandle), BOOL_TO_STR(m_bHandleExpect));
|
|
|
|
|
|
// 3、凸起候选实测特征(面积/填充率,仅用于日志排查)
|
|
|
for (size_t i = 0; i < protrusionList.size(); i++)
|
|
|
{
|
|
|
Handle_Candidate_ cand;
|
|
|
cand.protr = protrusionList[i];
|
|
|
// 左/右凸起:沿边为 y 方向,深度为 x 方向;上/下凸起相反
|
|
|
bool bHorz = (cand.protr.nSide == 0 || cand.protr.nSide == 1);
|
|
|
cand.nLen = bHorz ? cand.protr.roi.height : cand.protr.roi.width;
|
|
|
cand.nDepth = bHorz ? cand.protr.roi.width : cand.protr.roi.height;
|
|
|
m_HandleCandList.push_back(cand);
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle",
|
|
|
"protrusion %ld side %d roi [%d %d %d %d] len %d depth %d area %0.0f fill %0.2f",
|
|
|
i, cand.protr.nSide, cand.protr.roi.x, cand.protr.roi.y, cand.protr.roi.width, cand.protr.roi.height,
|
|
|
cand.nLen, cand.nDepth, cand.protr.fArea, cand.protr.fFillRatio);
|
|
|
}
|
|
|
|
|
|
// 4、逐个把手区域:在区域附近找宽高匹配的凸起(同一个凸起只匹配给一个区域)
|
|
|
std::vector<bool> usedList(m_HandleCandList.size(), false);
|
|
|
int nFound = 0;
|
|
|
for (size_t ir = 0; ir < regionBoxList.size(); ir++)
|
|
|
{
|
|
|
Handle_Region_Result_ result;
|
|
|
result.nIdx = (int)ir + 1;
|
|
|
result.boxRegion = regionBoxList[ir];
|
|
|
int nMatchIdx = MatchHandleRegion(m_HandleCandList, usedList, result.boxRegion, result.roiHandle);
|
|
|
result.bFound = (nMatchIdx >= 0);
|
|
|
if (result.bFound)
|
|
|
{
|
|
|
usedList[nMatchIdx] = true;
|
|
|
nFound++;
|
|
|
}
|
|
|
m_HandleRegionResultList.push_back(result);
|
|
|
|
|
|
std::string strResult = "handle missing !!";
|
|
|
if (result.bFound)
|
|
|
{
|
|
|
strResult = "handle roi [" + std::to_string(result.roiHandle.x) + " " + std::to_string(result.roiHandle.y) + " " +
|
|
|
std::to_string(result.roiHandle.width) + " " + std::to_string(result.roiHandle.height) + "]";
|
|
|
}
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "handle region %d box [%d %d %d %d] -> %s",
|
|
|
result.nIdx, result.boxRegion.x, result.boxRegion.y, result.boxRegion.width, result.boxRegion.height,
|
|
|
strResult.c_str());
|
|
|
}
|
|
|
m_bHandleFound = (!regionBoxList.empty() && nFound == (int)regionBoxList.size());
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, "Handle", "handle region num %ld found num %d -> %s",
|
|
|
regionBoxList.size(), nFound, m_bHandleFound ? "handle ok" : "handle missing !!");
|
|
|
|
|
|
// 5、存过程图:凸起红框、把手区域蓝框、匹配上的把手绿框(仅调试时)
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
{
|
|
|
cv::Mat show;
|
|
|
if (bigmask.channels() == 1)
|
|
|
{
|
|
|
cv::cvtColor(bigmask, show, cv::COLOR_GRAY2BGR);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
show = bigmask.clone();
|
|
|
}
|
|
|
for (size_t i = 0; i < m_HandleCandList.size(); i++)
|
|
|
{
|
|
|
cv::rectangle(show, m_HandleCandList[i].protr.roi, cv::Scalar(0, 0, 255), 8);
|
|
|
}
|
|
|
for (size_t i = 0; i < m_HandleRegionResultList.size(); i++)
|
|
|
{
|
|
|
cv::rectangle(show, m_HandleRegionResultList[i].boxRegion, cv::Scalar(255, 0, 0), 8);
|
|
|
if (m_HandleRegionResultList[i].bFound)
|
|
|
{
|
|
|
cv::rectangle(show, m_HandleRegionResultList[i].roiHandle, cv::Scalar(0, 255, 0), 12);
|
|
|
}
|
|
|
}
|
|
|
cv::Mat showSmall;
|
|
|
cv::resize(show, showSmall, cv::Size(show.cols / 4, show.rows / 4));
|
|
|
std::vector<int> paramJpg = {cv::IMWRITE_JPEG_QUALITY, 90};
|
|
|
cv::imwrite(DetImgInfo_shareP->strChannel + "_handle_det.jpg", showSmall, paramJpg);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// 标签检测(独立步骤):用 (图像 + 定位结果) 跑 Tag_Loc 模型
|
|
|
// 裁剪区域与定位流程里 Tag_Loc 的口径完全一致(定位得到的产品外接旋转矩形):
|
|
|
// 明场相机用自己的定位结果;暗场相机没做定位模型,用复用明场的结果 —— 保证暗/明场对同一摆位切出同样区域。
|
|
|
// 注意:标签模型对裁剪尺度很敏感(裁剪差 3px 就会让检出个数变化),不能另用 bigmask 重推裁剪框。
|
|
|
// bigmask 仅在定位外接矩形异常时作兜底(最大轮廓外接框)。
|
|
|
// 检出的标签 roi 存 m_tag_roiList(裁剪图坐标系,与定位流程 tagroi 的约定一致),由 CheckRun 统一上报 NG。
|
|
|
int ImgCheckAnalysisy::CheckTagByBigMask(const cv::Mat &img, const cv::Mat &bigmask)
|
|
|
{
|
|
|
m_tag_roiList.clear();
|
|
|
if (!AI_Factory || !AI_Factory->Tag_Loc)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "Tag_Loc model is null");
|
|
|
return 1;
|
|
|
}
|
|
|
if (img.empty())
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "tag check img is empty");
|
|
|
return 2;
|
|
|
}
|
|
|
|
|
|
// 1、裁剪区域:与定位流程 Tag_Loc 的裁剪口径一致(产品外接旋转矩形,暗场为复用明场的值)
|
|
|
cv::Rect roiCrop = m_outer_rroi.boundingRect() & cv::Rect(0, 0, img.cols, img.rows);
|
|
|
if (roiCrop.width <= 0 || roiCrop.height <= 0)
|
|
|
{
|
|
|
// 异常兜底:定位外接矩形无效时,用 bigmask 最大轮廓外接框
|
|
|
if (bigmask.empty())
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "tag check roi invalid and bigmask is empty");
|
|
|
return 3;
|
|
|
}
|
|
|
cv::Mat binMask;
|
|
|
{
|
|
|
cv::Mat grayMask;
|
|
|
if (bigmask.channels() != 1)
|
|
|
{
|
|
|
cv::cvtColor(bigmask, grayMask, cv::COLOR_BGR2GRAY);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
grayMask = bigmask;
|
|
|
}
|
|
|
double dMin = 0;
|
|
|
double dMax = 0;
|
|
|
cv::minMaxLoc(grayMask, &dMin, &dMax);
|
|
|
// 兼容 0/1 与 0/255 掩膜:取半强度作阈值(重采样后的边缘取到真实边界)
|
|
|
cv::threshold(grayMask, binMask, dMax > 1 ? dMax * 0.5 : 0, 255, cv::THRESH_BINARY);
|
|
|
}
|
|
|
bool bFound = false;
|
|
|
std::vector<cv::Point> contour = CheckUtil::getLargestContourROI(binMask, bFound);
|
|
|
if (!bFound || contour.size() < 3)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "tag check bigmask contour too few");
|
|
|
return 4;
|
|
|
}
|
|
|
roiCrop = cv::minAreaRect(contour).boundingRect() & cv::Rect(0, 0, img.cols, img.rows);
|
|
|
if (roiCrop.width <= 0 || roiCrop.height <= 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "tag check crop roi error [%d %d %d %d]", roiCrop.x, roiCrop.y, roiCrop.width, roiCrop.height);
|
|
|
return 5;
|
|
|
}
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Tag", "tag check use bigmask roi [%d %d %d %d]",
|
|
|
roiCrop.x, roiCrop.y, roiCrop.width, roiCrop.height);
|
|
|
}
|
|
|
|
|
|
// 2、裁剪 -> resize 到 Tag_Loc 模型输入尺寸 -> 灰度
|
|
|
std::shared_ptr<AIModel_Base> pTag = AI_Factory->Tag_Loc;
|
|
|
cv::Size sz(pTag->input_0.width, pTag->input_0.height);
|
|
|
if (sz.width <= 0 || sz.height <= 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "tag check invalid model input size");
|
|
|
return 6;
|
|
|
}
|
|
|
cv::Mat detImg;
|
|
|
cv::resize(img(roiCrop), detImg, sz);
|
|
|
if (detImg.channels() == 3)
|
|
|
{
|
|
|
cv::cvtColor(detImg, detImg, cv::COLOR_BGR2GRAY);
|
|
|
}
|
|
|
else if (detImg.channels() == 4)
|
|
|
{
|
|
|
cv::cvtColor(detImg, detImg, cv::COLOR_BGRA2GRAY);
|
|
|
}
|
|
|
|
|
|
// 3、推理
|
|
|
cv::Mat mask;
|
|
|
int re = pTag->AIDet(detImg, mask);
|
|
|
if (re != 0)
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "Tag_Loc AIDet error %d", re);
|
|
|
return 100 + re;
|
|
|
}
|
|
|
if (mask.empty())
|
|
|
{
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Tag", "tag mask empty, no tag");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// 调试存图(仅 -s 时保存)
|
|
|
if (DetImgInfo_shareP && DetImgInfo_shareP->bsaveProcessImg)
|
|
|
{
|
|
|
cv::imwrite(DetImgInfo_shareP->strChannel + "_tag_in.png", detImg);
|
|
|
cv::imwrite(DetImgInfo_shareP->strChannel + "_tag_out_mask.png", mask);
|
|
|
}
|
|
|
|
|
|
// 4、mask 轮廓 -> 还原到裁剪图坐标 -> RotatedRect(与定位流程 tagroi 的坐标系一致)
|
|
|
std::vector<std::vector<cv::Point>> contours;
|
|
|
std::vector<cv::Vec4i> hierarchy;
|
|
|
cv::findContours(mask.clone(), contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
|
|
const float scale_x = (float)roiCrop.width / (float)sz.width;
|
|
|
const float scale_y = (float)roiCrop.height / (float)sz.height;
|
|
|
for (size_t i = 0; i < contours.size(); i++)
|
|
|
{
|
|
|
if (contours[i].size() < 3 || cv::contourArea(contours[i]) <= 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
std::vector<cv::Point2f> srcContour;
|
|
|
srcContour.reserve(contours[i].size());
|
|
|
for (const auto &pt : contours[i])
|
|
|
{
|
|
|
srcContour.emplace_back(pt.x * scale_x, pt.y * scale_y);
|
|
|
}
|
|
|
m_tag_roiList.emplace_back(cv::minAreaRect(srcContour));
|
|
|
}
|
|
|
// 面积从大到小排序(与定位流程 tagroi 的顺序一致)
|
|
|
std::sort(m_tag_roiList.begin(), m_tag_roiList.end(),
|
|
|
[](const cv::RotatedRect &a, const cv::RotatedRect &b)
|
|
|
{ return a.size.width * a.size.height > b.size.width * b.size.height; });
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Tag", "tag region num %ld crop [%d %d %d %d] model [%d %d]",
|
|
|
m_tag_roiList.size(), roiCrop.x, roiCrop.y, roiCrop.width, roiCrop.height, sz.width, sz.height);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
// 在某个把手区域附近找宽高匹配的凸起
|
|
|
// 匹配条件:1、凸起中心落在把手区域附近(区域宽/高各外扩 HANDLE_NEAR_EXPAND_RATIO)
|
|
|
// 2、凸起宽高与该区域外接矩形宽高的比值都在 [HANDLE_SIZE_MIN_RATIO, HANDLE_SIZE_MAX_RATIO](允许横竖互换)
|
|
|
// 返回凸起在 candList 中的下标,-1 表示没找到
|
|
|
int ImgCheckAnalysisy::MatchHandleRegion(const std::vector<Handle_Candidate_> &candList, const std::vector<bool> &usedList,
|
|
|
const cv::Rect &boxRegion, cv::Rect &roiHandle)
|
|
|
{
|
|
|
if (boxRegion.width <= 0 || boxRegion.height <= 0)
|
|
|
{
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
// 搜索范围:把手区域外扩(宽/高各外扩区域尺寸的一半),容忍产品摆放偏差
|
|
|
int nx = std::max(10, (int)(boxRegion.width * HANDLE_NEAR_EXPAND_RATIO));
|
|
|
int ny = std::max(10, (int)(boxRegion.height * HANDLE_NEAR_EXPAND_RATIO));
|
|
|
cv::Rect searchBox(boxRegion.x - nx, boxRegion.y - ny, boxRegion.width + 2 * nx, boxRegion.height + 2 * ny);
|
|
|
cv::Point pRegionCenter(boxRegion.x + boxRegion.width / 2, boxRegion.y + boxRegion.height / 2);
|
|
|
|
|
|
int nBestIdx = -1;
|
|
|
double fBestDis = 1e18;
|
|
|
int nNearIdx = -1; // 区域内离区域中心最近的凸起(仅用于日志排查)
|
|
|
double fNearDis = 1e18;
|
|
|
for (size_t i = 0; i < candList.size(); i++)
|
|
|
{
|
|
|
if (i < usedList.size() && usedList[i])
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
const Handle_Candidate_ &cand = candList[i];
|
|
|
const cv::Rect &roi = cand.protr.roi;
|
|
|
if (roi.width <= 0 || roi.height <= 0)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
// 位置:凸起中心要落在把手区域附近
|
|
|
cv::Point pCenter(roi.x + roi.width / 2, roi.y + roi.height / 2);
|
|
|
if (!searchBox.contains(pCenter))
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
double fDis = CheckUtil::calDis(pCenter, pRegionCenter);
|
|
|
if (fDis < fNearDis)
|
|
|
{
|
|
|
fNearDis = fDis;
|
|
|
nNearIdx = (int)i;
|
|
|
}
|
|
|
|
|
|
// 宽高:与把手区域外接矩形的宽高相近(允许横竖互换)
|
|
|
float fw = (float)roi.width;
|
|
|
float fh = (float)roi.height;
|
|
|
bool bSizeOK = (fw >= boxRegion.width * HANDLE_SIZE_MIN_RATIO && fw <= boxRegion.width * HANDLE_SIZE_MAX_RATIO &&
|
|
|
fh >= boxRegion.height * HANDLE_SIZE_MIN_RATIO && fh <= boxRegion.height * HANDLE_SIZE_MAX_RATIO);
|
|
|
if (!bSizeOK)
|
|
|
{
|
|
|
bSizeOK = (fw >= boxRegion.height * HANDLE_SIZE_MIN_RATIO && fw <= boxRegion.height * HANDLE_SIZE_MAX_RATIO &&
|
|
|
fh >= boxRegion.width * HANDLE_SIZE_MIN_RATIO && fh <= boxRegion.width * HANDLE_SIZE_MAX_RATIO);
|
|
|
}
|
|
|
if (!bSizeOK)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if (fDis < fBestDis)
|
|
|
{
|
|
|
fBestDis = fDis;
|
|
|
nBestIdx = (int)i;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (nBestIdx < 0 && nNearIdx >= 0)
|
|
|
{
|
|
|
// 没匹配上时打印区域内最近的凸起,便于排查阈值
|
|
|
const cv::Rect &roi = candList[nNearIdx].protr.roi;
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle",
|
|
|
"region box [%d %d %d %d] no match, nearest protrusion roi [%d %d %d %d] w/h ratio %0.2f %0.2f",
|
|
|
boxRegion.x, boxRegion.y, boxRegion.width, boxRegion.height,
|
|
|
roi.x, roi.y, roi.width, roi.height,
|
|
|
(float)roi.width / (float)boxRegion.width, (float)roi.height / (float)boxRegion.height);
|
|
|
}
|
|
|
|
|
|
if (nBestIdx >= 0)
|
|
|
{
|
|
|
roiHandle = candList[nBestIdx].protr.roi;
|
|
|
}
|
|
|
return nBestIdx;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::CalProductSize()
|
|
|
{
|
|
|
float fw = m_outer_roi.width * m_fImgage_Scale_X;
|
|
|
float fh = m_outer_roi.height * m_fImgage_Scale_Y;
|
|
|
fw = std::ceil(fw * 10) / 10;
|
|
|
fh = std::ceil(fh * 10) / 10;
|
|
|
m_CheckResult_shareP->productWidht_mm = fw;
|
|
|
m_CheckResult_shareP->productHeight_mm = fh;
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, "Det ROI", " [w %d,h %d] (piexl) -> [w %f h %f](mm),scale %f %f",
|
|
|
m_outer_roi.width, m_outer_roi.height, fw, fh, m_fImgage_Scale_X, m_fImgage_Scale_Y);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int ImgCheckAnalysisy::ImgPreDet()
|
|
|
{
|
|
|
|
|
|
// 计算产品尺寸
|
|
|
CalProductSize();
|
|
|
|
|
|
if (m_pbaseCheckFunction->saveImg.bSaveClsImg)
|
|
|
{
|
|
|
creatsavedir();
|
|
|
}
|
|
|
|
|
|
// 裁剪图(_RE_CutImg)保留原图通道:源图为彩色时用彩色裁剪图,否则回退灰度检测图
|
|
|
m_CheckResult_shareP->cutSrcimg = m_pImageAllResult->detColorImg.empty()
|
|
|
? m_pImageAllResult->detImg
|
|
|
: m_pImageAllResult->detColorImg;
|
|
|
ResizeImg();
|
|
|
|
|
|
m_pImageAllResult->fscale_detToresult_x = m_pImageAllResult->resultImg.cols * 1.0f / m_pImageAllResult->detImg.cols;
|
|
|
m_pImageAllResult->fscale_detToresult_y = m_pImageAllResult->resultImg.rows * 1.0f / m_pImageAllResult->detImg.rows;
|
|
|
return 0;
|
|
|
}
|