feat 初步添加暗场复用明场定位结果

dev_lsy
liusiyang 2 months ago
parent 92850246a4
commit 12404cc400

@ -0,0 +1,141 @@
#ifndef BRIGHT_GEOSHARE_HPP_
#define BRIGHT_GEOSHARE_HPP_
// ============================================================================
// 明场(B) -> 暗场(D) 几何结果共享
//
// 背景:本机 4 台相机 BCA/DCA/BTA/DTA 中,B 与 D 是同一工位的两个光照通道
// (配对:BCA<->DCA, BTA<->DTA),产品/物料在图像中的位置一致,仅明暗场不同。
// 暗场图对比度低,定位(GetEdgeRoi/AI_Edge)与参数自适应(Adapt)不稳定,
// 因此 D 相机直接复用 B 相机算出的:
// 1) 定位结果 m_CutRoi(检测裁切框)
// 2) Adapt 目标:产品旋转外接矩形(rot_cur_roi)
// 3) 产品真实宽高(mm)
//
// 用法:
// Bright 侧(BCA/BTA)在几何计算完成后 Publish 快照;
// Dark 侧(DCA/DTA)在需要时 Take 快照(带超时,拿不到则回退自身逻辑)。
//
// 匹配键 = 工位对("CA"/"TA"),不按产品名匹配:实际流程中四个相机可能以各自独立
// 的“产品”送检(如 ET 套图流程按 BCA→BTA→DCA→DTA 顺序各送一次),B 与 D 的产品名
// 并不相同。取用即消费(Take 成功后删除),保证一份明场结果只服务一次暗场,
// 不会把上一块板的坐标复用到下一块板;键集合只有 CA/TA 两个,永不增长。
//
// 线程安全:内部 mutex + condition_variable,可在多相机线程间并发使用。
// 关闭开关:环境变量 AOI_DISABLE_BRIGHT_DARK_SHARE=1(用于 A/B 对比旧行为)。
// ============================================================================
#include <string>
#include <map>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <cstdlib>
#include <opencv2/opencv.hpp>
// 等待明场结果的超时(ms):超时后暗场回退到自身定位/自适应逻辑。
// 明场与暗场同时开始检查,该值只需覆盖明场“拼接 + 粗定位 + Adapt + AI_Edge”的耗时。
static const long kBrightGeoWaitTimeoutMs = 60000;
// 明暗场共享开关(默认开启)
inline bool BrightGeoShareEnabled()
{
static const bool bEnabled = []() -> bool {
const char *pEnv = std::getenv("AOI_DISABLE_BRIGHT_DARK_SHARE");
if (pEnv == nullptr)
{
return true;
}
std::string s(pEnv);
return !(s == "1" || s == "true" || s == "TRUE" || s == "on" || s == "ON");
}();
return bEnabled;
}
// 相机代码(BCA/BTA/DCA/DTA) -> 工位对(CA/TA);非预期输入返回空串
inline std::string BrightGeoPairOfCamera(const std::string &strCameraCode)
{
if (strCameraCode.find("CA") != std::string::npos)
{
return "CA";
}
if (strCameraCode.find("TA") != std::string::npos)
{
return "TA";
}
return "";
}
// 是否明场相机(代码以 B 开头,如 BCA/BTA)
inline bool BrightGeoCameraIsBright(const std::string &strCameraCode)
{
return !strCameraCode.empty() && (strCameraCode[0] == 'B' || strCameraCode[0] == 'b');
}
// 是否暗场相机(代码以 D 开头,如 DCA/DTA)
inline bool BrightGeoCameraIsDark(const std::string &strCameraCode)
{
return !strCameraCode.empty() && (strCameraCode[0] == 'D' || strCameraCode[0] == 'd');
}
// 共享快照的键:工位对("CA"/"TA")
// (注:不按产品名匹配,原因见文件头说明)
// 明场发布给暗场的几何快照
struct BrightGeoSnapshot
{
std::string strProduct; // 发布该快照的产品名(仅用于日志追踪)
bool bValid = false; // 快照是否可用于暗场(明场定位流程是否正常完成)
bool bRoiValid = false; // 产品旋转外接矩形是否有效(明场 Adapt 是否成功)
cv::RotatedRect rotRoi; // 产品旋转外接矩形(原图坐标)
cv::Rect cutRoi; // 定位裁切框(原图坐标)
cv::Size imgSize; // 明场拼接图尺寸(暗场做一致性校验)
float productWidth_mm = 0.f; // 产品真实宽度(mm)
float productHeight_mm = 0.f; // 产品真实高度(mm)
};
class BrightGeoShare
{
public:
static BrightGeoShare &Instance()
{
static BrightGeoShare s_ins;
return s_ins;
}
// 明场发布(同一工位对覆盖旧值),唤醒等待中的暗场线程
void Publish(const std::string &strPair, const BrightGeoSnapshot &snap)
{
{
std::lock_guard<std::mutex> lk(m_mtx);
m_map[strPair] = snap;
}
m_cv.notify_all();
}
// 暗场等待并取走明场结果(超时返回 false;成功则取走并删除,一份结果只服务一次)
bool Take(const std::string &strPair, BrightGeoSnapshot &snap, long nTimeoutMs)
{
std::unique_lock<std::mutex> lk(m_mtx);
bool bOk = m_cv.wait_for(lk, std::chrono::milliseconds(nTimeoutMs),
[&]() { return m_map.find(strPair) != m_map.end(); });
if (!bOk)
{
return false;
}
auto it = m_map.find(strPair);
snap = it->second;
m_map.erase(it);
return true;
}
private:
BrightGeoShare() {}
~BrightGeoShare() {}
std::mutex m_mtx;
std::condition_variable m_cv;
std::map<std::string, BrightGeoSnapshot> m_map;
};
#endif

@ -37,6 +37,7 @@
#include "ImageAllResult.h"
#include "Task.h"
#include "TcsCheck.h"
#include "BrightGeoShare.hpp"
using namespace std;
using namespace cv;
@ -118,6 +119,10 @@ private:
int AI_Edge(const cv::Mat &img, cv::Rect &cutRoi);
// 计算产品尺寸
int CalProductSize();
// 计算产品物理宽高(mm)(未取整前的原始换算 + 现有格式取整),供 CalProductSize 与明场发布共用
void ComputeProductSizeValues(float &fw, float &fh, float &fhPixel);
// 解析当前检测图的相机代码(BCA/BTA/DCA/DTA),无法解析返回空串
std::string GetCurCameraCode();
// 图片预处理
int ImgPreDet();
@ -203,7 +208,8 @@ private:
int BLobToDetResult();
//自适应参数区域更新
int Adapt_Config(Mat img, Rect cur_roi, cv::RotatedRect& rot_cur_roi, bool b_update);
// pTargetRoi != nullptr 时使用外部(明场相机)提供的产品旋转矩形作为仿射目标,跳过自身边缘定位
int Adapt_Config(Mat img, Rect cur_roi, cv::RotatedRect& rot_cur_roi, bool b_update, const cv::RotatedRect *pTargetRoi = nullptr);
std::shared_ptr<AIFactory> AI_Factory;
@ -247,6 +253,10 @@ private:
cv::RotatedRect m_rot_cur_roi; // 产品旋转外接矩形(原图坐标)
bool m_bRotCurRoiValid = false; // m_rot_cur_roi 是否有效
// 暗场(DCA/DTA)复用明场(BCA/BTA)几何结果(定位/Adapt目标/产品宽高),详见 BrightGeoShare.hpp
bool m_bDarkUseBrightGeo = false; // 本次检测暗场是否成功拿到明场结果
BrightGeoSnapshot m_brightGeoSnap; // 暗场复用的明场结果快照
std::string m_strCurDetChannel; // 当前处理的图片通道
// // 检测控制参数

@ -5,11 +5,14 @@
// 【临时补丁】产品长度方向(y) 像素 -> 物理毫米 的分段换算
//
// 背景:产品沿长度方向运动/扫描时速率不恒定(尾端有拉伸),像素↔mm 不是单一
// 比例,实测标定出 5 段,每段斜率不同。
// 比例,实测标定出 6 段,每段斜率不同。
//
// 分段表(分段点 = 像素,**以产品顶边为 0 点**;斜率为 mm/px)见函数内
// yPx1..yPx5 / pmm1..pmm5,改标定只需改那一段(此处不重复写数值,避免两处不同步)。
// yPx0..yPx5 / pmm0..pmm5(共 6 段),改标定只需改那一段
// (此处不重复写数值,避免两处不同步)。
// 分段点处前后两段取值相同,函数连续,不会在分段点跳变。
// 5000px 是最前端新加的分段点:把原来的 0~10928 拆成 0~5000 与 5000~10928,
// 当 pmm0 == pmm1 == 0.016869 时与拆分前逐位等价。
//
// 共用方(必须共用同一张表,否则"产品高度"和"缺陷坐标"会各用一把尺子):
// 1) 缺陷 y 物理坐标:ImageResultJudge::BuildDefectImage()
@ -20,33 +23,43 @@
inline float PixelYToMm(float fYPixel)
{
// ================= 标定参数:改标定只需改这一段 =================
// 各段斜率(mm/px)
const float pmm1 = 0.016869;
const float pmm2 = 0.016463;
const float pmm3 = 0.016220;
const float pmm4 = 0.015669;
const float pmm5 = 0.014337;
// 各段斜率(mm/px),下标 0..5 依次对应 6 个分段
// 注:pmm0 / pmm1 是新增分段点 5000 拆出来的两段,标定值待填;
// 两者都取原 0~10928 段的 0.016869 时,与拆分前结果逐位相同。
const float pmm0 = 0.016869; // 0 ~ 5000
const float pmm1 = 0.016869; // 5000 ~ 10928
const float pmm2 = 0.016463; // 10928 ~ 17002
const float pmm3 = 0.016220; // 17002 ~ 23167
const float pmm4 = 0.015669; // 23167 ~ 29549
const float pmm5 = 0.014337; // >= 29549
// 各段起始像素(以产品顶边为 0 点)
const float yPx1 = 0.0f;
const float yPx0 = 0.0f;
const float yPx1 = 5000.0f;
const float yPx2 = 10928.0f;
const float yPx3 = 17002.0f;
const float yPx4 = 23167.0f;
const float yPx5 = 29549.0f;
// 各段像素长度(px)
const float dPx0 = yPx1 - yPx0;
const float dPx1 = yPx2 - yPx1;
const float dPx2 = yPx3 - yPx2;
const float dPx3 = yPx4 - yPx3;
const float dPx4 = yPx5 - yPx4;
// 各段末端的累计物理长度(mm)
const float d1 = dPx1 * pmm1;
const float d0 = dPx0 * pmm0;
const float d1 = d0 + dPx1 * pmm1;
const float d2 = d1 + dPx2 * pmm2;
const float d3 = d2 + dPx3 * pmm3;
const float d4 = d3 + dPx4 * pmm4;
if (fYPixel < yPx1)
{
return fYPixel * pmm0;
}
if (fYPixel < yPx2)
{
return fYPixel * pmm1;
return (fYPixel - yPx1) * pmm1 + d0;
}
if (fYPixel < yPx3)
{

@ -610,7 +610,7 @@ std::vector<cv::Point2f> sort_vertices(cv::RotatedRect rrect) {
return vertices;
}
int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, cv::RotatedRect& rot_cur_roi, bool b_update){
int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, cv::RotatedRect& rot_cur_roi, bool b_update, const cv::RotatedRect *pTargetRoi){
if (img.empty()) {
std::cerr << "Error: Input image 'img' is empty!" << std::endl;
return -1;
@ -626,10 +626,20 @@ int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, cv::RotatedRect& rot_
{
strCameraName = DetImgInfo_shareP->strCameraName;
}
if (pTargetRoi != nullptr)
{
// 暗场复用明场:以明场检测到的产品旋转矩形作为仿射目标,跳过暗场自身的边缘定位(GetEdgeRoi)
rot_cur_roi = *pTargetRoi;
m_pdetlog->AddCheckstr(PrintLevel_0, "Adapt_Config", "use external target (Bright rot_roi) center(%.1f, %.1f) size(%.1f, %.1f) angle %.2f",
rot_cur_roi.center.x, rot_cur_roi.center.y, rot_cur_roi.size.width, rot_cur_roi.size.height, rot_cur_roi.angle);
}
else
{
int get_edge_roi = GetEdgeRoi(img, cur_roi, rot_cur_roi, 20, 20, bSaveProcessImg, strCameraName);
if(get_edge_roi != 0){
return 2;
}
}
m_pdetlog->AddCheckstr(PrintLevel_0, "Adapt_Config", "-------------------start--------------");
/*计算xy偏移,缩放比例*/
@ -830,10 +840,69 @@ int ImgCheckAnalysisy::CheckRun()
m_CheckResult_shareP->basicResult.strChannel = m_CheckResult_shareP->in_shareImage->strChannel;
m_strCurDetChannel = m_CheckResult_shareP->basicResult.strChannel;
// 相机角色:BCA<->DCA、BTA<->DTA 为同一工位的明/暗场通道,产品位置一致。
// 暗场图对比度低,定位与参数自适应不稳定,故 DCA/DTA 复用 BCA/BTA 的定位/Adapt/产品宽高
// 结果(经 BrightGeoShare 传递;拿不到时回退原逻辑)。
const std::string strCamCode = GetCurCameraCode();
const bool bDarkCam = BrightGeoCameraIsDark(strCamCode) && !BrightGeoPairOfCamera(strCamCode).empty();
const bool bBrightCam = BrightGeoCameraIsBright(strCamCode) && !BrightGeoPairOfCamera(strCamCode).empty();
m_bDarkUseBrightGeo = false;
m_brightGeoSnap = BrightGeoSnapshot();
/*自适应更新参数*/
long time_adapt_s = CheckUtil::getcurTime();
cv::RotatedRect rot_cur_roi;
int nAdaptRe = Adapt_Config(m_CheckResult_shareP->in_shareImage->img, m_CutRoi, rot_cur_roi, m_pbaseCheckFunction->markLine.badapt_region);
int nAdaptRe = 1;
// 暗场:等待并复用明场的几何结果(带超时;超时/不可用时回退自身逻辑)
bool bUseBrightGeo = false;
if (bDarkCam && BrightGeoShareEnabled())
{
BrightGeoSnapshot snap;
const std::string strPair = BrightGeoPairOfCamera(strCamCode);
long tw_s = CheckUtil::getcurTime();
bool bGot = BrightGeoShare::Instance().Take(strPair, snap, kBrightGeoWaitTimeoutMs);
long tw_e = CheckUtil::getcurTime();
if (bGot && snap.bValid && snap.imgSize == m_CheckResult_shareP->in_shareImage->img.size())
{
bUseBrightGeo = true;
m_brightGeoSnap = snap;
m_pdetlog->AddCheckstr(PrintLevel_0, "BrightDark", "Dark[%s] use Bright[%s] pair[%s] wait %ld ms", strCamCode.c_str(), snap.strProduct.c_str(), strPair.c_str(), tw_e - tw_s);
}
else if (bGot)
{
m_pdetlog->AddCheckstr(PrintLevel_0, "BrightDark", "Dark[%s] Bright[%s] result unusable(valid %d, imgSize %dx%d vs %dx%d), fallback",
strCamCode.c_str(), snap.strProduct.c_str(), snap.bValid, snap.imgSize.width, snap.imgSize.height,
m_CheckResult_shareP->in_shareImage->img.cols, m_CheckResult_shareP->in_shareImage->img.rows);
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_0, "BrightDark", "Dark[%s] wait Bright timeout(%ld ms), fallback", strCamCode.c_str(), kBrightGeoWaitTimeoutMs);
}
}
if (bUseBrightGeo)
{
// 1) 参数自适应:以明场产品旋转矩形为仿射目标(跳过暗场自身定位)
m_bDarkUseBrightGeo = true;
rot_cur_roi = m_brightGeoSnap.rotRoi;
m_bRotCurRoiValid = m_brightGeoSnap.bRoiValid;
if (m_brightGeoSnap.bRoiValid)
{
m_rot_cur_roi = m_brightGeoSnap.rotRoi;
nAdaptRe = Adapt_Config(m_CheckResult_shareP->in_shareImage->img, m_CutRoi, rot_cur_roi,
m_pbaseCheckFunction->markLine.badapt_region, &m_brightGeoSnap.rotRoi);
m_pdetlog->AddCheckstr(PrintLevel_0, "BrightDark", "Dark[%s] Adapt with Bright target ret = %d", strCamCode.c_str(), nAdaptRe);
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_0, "BrightDark", "Dark[%s] Bright rot_roi invalid, skip Adapt (regions keep current)", strCamCode.c_str());
}
}
else
{
nAdaptRe = Adapt_Config(m_CheckResult_shareP->in_shareImage->img, m_CutRoi, rot_cur_roi, m_pbaseCheckFunction->markLine.badapt_region);
if (nAdaptRe == 0)
{
m_rot_cur_roi = rot_cur_roi;
@ -843,6 +912,7 @@ int ImgCheckAnalysisy::CheckRun()
{
m_bRotCurRoiValid = false;
}
}
long time_adapt_e = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_0, "Adapt_Config", "time = %ld", time_adapt_e - time_adapt_s);
@ -857,20 +927,48 @@ int ImgCheckAnalysisy::CheckRun()
}
long time_edge_s = CheckUtil::getcurTime();
// 3、AI 边缘定位
if(m_CheckResult_shareP->basicResult.strChannel == "DCA" || m_CheckResult_shareP->basicResult.strChannel == "DTA")
if(bDarkCam)
{
const cv::Mat &imgCur = m_CheckResult_shareP->in_shareImage->img;
const cv::Rect rectImg(0, 0, imgCur.cols, imgCur.rows);
if (m_bDarkUseBrightGeo)
{
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "Not Use AI_Edge %s", m_CheckResult_shareP->basicResult.strChannel.c_str());
// 定位:直接采用明场相机的定位裁切框(B/D 同一工位,图像坐标一致;尺寸已在取快照时校验)
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "Not Use AI_Edge %s (use Bright cutRoi)", strCamCode.c_str());
m_CutRoi = m_brightGeoSnap.cutRoi & rectImg;
if (m_CutRoi.width <= 0 || m_CutRoi.height <= 0)
{
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "Error Bright cutRoi invalid after clamp, fallback to local rot_cur_roi");
const cv::Rect rt = rot_cur_roi.boundingRect();
m_CutRoi = cv::Rect(rt.x - 50, rt.y - 50, rt.width + 100, rt.height + 100) & rectImg;
}
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "CutRoi = [%d, %d, %d, %d]", m_CutRoi.x, m_CutRoi.y, m_CutRoi.width, m_CutRoi.height);
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "Not Use AI_Edge %s", strCamCode.c_str());
m_CutRoi = cv::Rect(rot_cur_roi.boundingRect().x - 50 , rot_cur_roi.boundingRect().y - 50, rot_cur_roi.boundingRect().width + 100, rot_cur_roi.boundingRect().height + 100)
& cv::Rect(0, 0, m_CheckResult_shareP->in_shareImage->img.cols, m_CheckResult_shareP->in_shareImage->img.rows);
& rectImg;
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "rot_cur_roi = [%d, %d, %d, %d]", rot_cur_roi.boundingRect().x, rot_cur_roi.boundingRect().y, rot_cur_roi.boundingRect().width, rot_cur_roi.boundingRect().height);
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "CutRoi = [%d, %d, %d, %d]", m_CutRoi.x, m_CutRoi.y, m_CutRoi.width, m_CutRoi.height);
}
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "Use AI_Edge %s", m_CheckResult_shareP->basicResult.strChannel.c_str());
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "Use AI_Edge %s", strCamCode.c_str());
int reedge = AI_Edge(m_CheckResult_shareP->in_shareImage->img, m_CutRoi);
if (reedge != 0)
{
// 明场失败时也要尽快通知暗场,避免暗场一直等到超时
if (bBrightCam && BrightGeoShareEnabled())
{
BrightGeoSnapshot snapFail;
snapFail.strProduct = m_CheckResult_shareP->in_shareImage->strImgProductID;
snapFail.bValid = false;
const std::string strPair = BrightGeoPairOfCamera(strCamCode);
BrightGeoShare::Instance().Publish(strPair, snapFail);
m_pdetlog->AddCheckstr(PrintLevel_0, "BrightDark", "Bright[%s] publish failure(pair[%s])", strCamCode.c_str(), strPair.c_str());
}
m_pdetlog->AddCheckstr(PrintLevel_0, "Error", "AI_Edge is error type = %d", reedge);
m_nErrorCode = reedge;
m_nCheckResultErrorCode = m_nErrorCode;
@ -878,6 +976,27 @@ int ImgCheckAnalysisy::CheckRun()
}
}
// 明场(BCA/BTA):发布定位/Adapt目标/产品宽高,供同工位暗场(DCA/DTA)复用
if (bBrightCam && BrightGeoShareEnabled())
{
BrightGeoSnapshot snap;
snap.strProduct = m_CheckResult_shareP->in_shareImage->strImgProductID;
snap.bValid = true;
snap.bRoiValid = m_bRotCurRoiValid;
snap.rotRoi = m_rot_cur_roi; // bRoiValid=false 时此值无效,暗场不会使用
snap.cutRoi = m_CutRoi;
snap.imgSize = m_CheckResult_shareP->in_shareImage->img.size();
float fw = 0.f, fh = 0.f, fhPixel = 0.f;
ComputeProductSizeValues(fw, fh, fhPixel);
snap.productWidth_mm = fw;
snap.productHeight_mm = fh;
const std::string strPair = BrightGeoPairOfCamera(strCamCode);
BrightGeoShare::Instance().Publish(strPair, snap);
m_pdetlog->AddCheckstr(PrintLevel_0, "BrightDark", "Bright[%s] publish pair[%s] product[%s] rotRoiValid %d cutRoi[%d,%d,%d,%d] product %f x %f mm (hPixel %f)",
strCamCode.c_str(), strPair.c_str(), snap.strProduct.c_str(), snap.bRoiValid,
snap.cutRoi.x, snap.cutRoi.y, snap.cutRoi.width, snap.cutRoi.height, fw, fh, fhPixel);
}
long 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);
@ -2823,10 +2942,12 @@ int ImgCheckAnalysisy::AI_Edge(const cv::Mat &img, cv::Rect &cutRoi)
return re;
}
int ImgCheckAnalysisy::CalProductSize()
// 计算产品物理宽高(mm):旋转矩形有效时用旋转矩形边长,退化时用裁切框。
// 宽度用成像精度换算、高度走长度方向分段像素->mm 补丁(ImgMmPatch.hpp),
// 结果按现有格式取整到 0.1mm。供 CalProductSize 与明场快照发布共用,保证两处数值一致。
void ImgCheckAnalysisy::ComputeProductSizeValues(float &fw, float &fh, float &fhPixel)
{
float fw, fh;
float fhPixel = 0.f; // 产品沿长度方向(y)的像素高度,供长度方向分段补丁换算用
fhPixel = 0.f;
if (m_bRotCurRoiValid)
{
// 产品存在旋转角:用旋转矩形四个顶点的实际边长换算物理尺寸
@ -2857,6 +2978,66 @@ int ImgCheckAnalysisy::CalProductSize()
fh = PixelYToMm(fhPixel);
fw = std::ceil(fw * 10) / 10;
fh = std::ceil(fh * 10) / 10;
}
// 解析当前检测图的相机代码(BCA/BTA/DCA/DTA);无法解析返回空串。
// 依次尝试:配置相机名 -> shareImage 相机名 -> shareImage 通道名 -> 结果通道名(兼容不同命名来源)。
std::string ImgCheckAnalysisy::GetCurCameraCode()
{
std::vector<std::string> candidates;
candidates.push_back(m_AnalysisyConfig.commonCheckConfig.baseConfig.strCamearName);
if (DetImgInfo_shareP != nullptr)
{
candidates.push_back(DetImgInfo_shareP->strCameraName);
candidates.push_back(DetImgInfo_shareP->strChannel);
}
if (m_CheckResult_shareP != nullptr)
{
candidates.push_back(m_CheckResult_shareP->basicResult.strChannel);
}
static const char *kCameraCodes[] = {"BCA", "BTA", "DCA", "DTA"};
for (const std::string &cand : candidates)
{
if (cand.empty())
{
continue;
}
// 统一转大写,避免大小写差异
std::string strUpper = cand;
for (size_t ci = 0; ci < strUpper.size(); ci++)
{
if (strUpper[ci] >= 'a' && strUpper[ci] <= 'z')
{
strUpper[ci] = (char)(strUpper[ci] - 'a' + 'A');
}
}
for (size_t ci = 0; ci < sizeof(kCameraCodes) / sizeof(kCameraCodes[0]); ci++)
{
if (strUpper.find(kCameraCodes[ci]) != std::string::npos)
{
return std::string(kCameraCodes[ci]);
}
}
}
return std::string();
}
int ImgCheckAnalysisy::CalProductSize()
{
float fw, fh;
float fhPixel = 0.f; // 产品沿长度方向(y)的像素高度,供长度方向分段补丁换算用
if (m_bDarkUseBrightGeo)
{
// 暗场:产品真实宽高直接采用明场相机的计算结果(B/D 同一工位,物理尺寸一致)
fw = m_brightGeoSnap.productWidth_mm;
fh = m_brightGeoSnap.productHeight_mm;
m_pdetlog->AddCheckstr(PrintLevel_0, "BrightDark", "Dark product size from Bright: w %f h %f (mm)", fw, fh);
}
else
{
ComputeProductSizeValues(fw, fh, fhPixel);
}
m_CheckResult_shareP->productWidht_mm = fw;
m_CheckResult_shareP->productHeight_mm = fh;

Loading…
Cancel
Save