|
|
|
|
@ -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,9 +626,19 @@ int ImgCheckAnalysisy::Adapt_Config(Mat img, Rect cur_roi, cv::RotatedRect& rot_
|
|
|
|
|
{
|
|
|
|
|
strCameraName = DetImgInfo_shareP->strCameraName;
|
|
|
|
|
}
|
|
|
|
|
int get_edge_roi = GetEdgeRoi(img, cur_roi, rot_cur_roi, 20, 20, bSaveProcessImg, strCameraName);
|
|
|
|
|
if(get_edge_roi != 0){
|
|
|
|
|
return 2;
|
|
|
|
|
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--------------");
|
|
|
|
|
|
|
|
|
|
@ -830,18 +840,78 @@ 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);
|
|
|
|
|
if (nAdaptRe == 0)
|
|
|
|
|
int nAdaptRe = 1;
|
|
|
|
|
|
|
|
|
|
// 暗场:等待并复用明场的几何结果(带超时;超时/不可用时回退自身逻辑)
|
|
|
|
|
bool bUseBrightGeo = false;
|
|
|
|
|
if (bDarkCam && BrightGeoShareEnabled())
|
|
|
|
|
{
|
|
|
|
|
m_rot_cur_roi = rot_cur_roi;
|
|
|
|
|
m_bRotCurRoiValid = true;
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
m_bRotCurRoiValid = false;
|
|
|
|
|
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;
|
|
|
|
|
m_bRotCurRoiValid = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Edge", "Not Use AI_Edge %s", m_CheckResult_shareP->basicResult.strChannel.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);
|
|
|
|
|
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);
|
|
|
|
|
const cv::Mat &imgCur = m_CheckResult_shareP->in_shareImage->img;
|
|
|
|
|
const cv::Rect rectImg(0, 0, imgCur.cols, imgCur.rows);
|
|
|
|
|
if (m_bDarkUseBrightGeo)
|
|
|
|
|
{
|
|
|
|
|
// 定位:直接采用明场相机的定位裁切框(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)
|
|
|
|
|
& 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;
|
|
|
|
|
|
|
|
|
|
|