update 优化pin整体偏移计算方式

dev_lsy
liusiyang 2 weeks ago
parent 211296fc20
commit 050dd31240

@ -18,11 +18,24 @@
#include <fstream>
// ===================== Pin 整体偏移估计参数 =====================
static const double PIN_SHIFT_MIN_SUPPORT_RATIO = 0.80; // 判定整体偏移所需的最小支持度(支持pin数 / 模板pin总数)
static const double PIN_SHIFT_MIN_SUPPORT_RATIO = 0.80; // 判定整体偏移所需的最小支持度(支持pin数 / 该排模板pin数)
static const float PIN_SHIFT_BIN_SIZE_PX = 4.0f; // 位移投票的网格尺寸(px), 同时作为峰值邻域半径
static const float PIN_SHIFT_MAX_GAP_RATIO = 0.5f; // 整体位移量级上限 = 相邻pin最小间距 * 该比例
// ======================================================================================
// 计算点相对旋转矩形“长边轴线”的带符号法向距离(检测图坐标系):
// 取矩形长边方向作为 Pin 排方向, 返回点沿短边(法线)方向相对矩形中心的投影;
// 正负号天然把分布在矩形两侧的 Pin 分成两组(上下两排分别估计整体偏移)。
static float PinRowSideOfRect(const cv::RotatedRect &rr, const cv::Point2f &pt)
{
const float th = static_cast<float>(rr.angle * CV_PI / 180.0);
// OpenCV RotatedRect(图像坐标系, y 轴向下): 宽度方向 (cos, -sin), 高度方向 (sin, cos)
const cv::Point2f widthDir(std::cos(th), -std::sin(th));
const cv::Point2f heightDir(std::sin(th), std::cos(th));
const cv::Point2f normalDir = (rr.size.width >= rr.size.height) ? heightDir : widthDir;
return (pt - rr.center).dot(normalDir);
}
// 分类映射配置文件(每行:label索引:缺陷名称;冒号后名称为该缺陷类型名称,行的顺序即缺陷类型索引)
static const std::string CLS_LABEL_CONFIG_FILE = "/home/aidlux/BOE/UseModel_ZB/KK_cls.txt";
@ -2349,53 +2362,108 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
std::vector<bool> curUsed(nCur, false);
// 整体偏移估计与消除(阈值见文件顶部 PIN_SHIFT_* 常量):
// 若足够多的模板 pin 都具有方向一致、量级接近的位移, 则把模板 pin 轮廓整体平移到与检测 pin 一致的位置,
// 后续匹配/偏移判据均基于平移后的模板 pin
{
// 相邻 pin 最小间距: 用于限制整体偏移的量级(整体偏移不应接近一个 pin 间距, 否则无法与"错配一列"区分)
float minGap = 0.0f;
// inner 定位 ROI 转到 detImg 坐标系(detImg 由 m_outer_roi 裁剪得到)
cv::RotatedRect innerRectDet = m_inner_rroi;
innerRectDet.center.x -= static_cast<float>(m_outer_roi.x);
innerRectDet.center.y -= static_cast<float>(m_outer_roi.y);
// 1) 用 inner ROI 的长边法线方向区分两组 pin: 法向坐标 < 0 为一排, >= 0 为另一排
std::vector<std::vector<int>> shiftGroups;
std::vector<std::string> shiftGroupNames;
if (m_inner_rroi.size.width > 0 && m_inner_rroi.size.height > 0)
{
std::vector<std::vector<int>> sideIdx(2);
for (int i = 0; i < nTpl; i++)
{
for (int j = i + 1; j < nTpl; j++)
sideIdx[(PinRowSideOfRect(innerRectDet, tplCenters[i]) < 0.0f) ? 0 : 1].push_back(i);
}
// 两排各自至少 2 个 pin 才能独立可靠估计; 否则回退为整体估计
if (sideIdx[0].size() >= 2 && sideIdx[1].size() >= 2)
{
float d = static_cast<float>(cv::norm(tplCenters[i] - tplCenters[j]));
if (minGap <= 0.0f || d < minGap)
shiftGroups.push_back(sideIdx[0]);
shiftGroupNames.push_back("upper row");
shiftGroups.push_back(sideIdx[1]);
shiftGroupNames.push_back("lower row");
}
else
{
minGap = d;
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Pin Check",
"pin row split by inner roi invalid: upper %d lower %d, estimate as a whole",
static_cast<int>(sideIdx[0].size()), static_cast<int>(sideIdx[1].size()));
}
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Pin Check",
"inner roi invalid, pin offset estimate as a whole");
}
if (shiftGroups.empty())
{
// inner ROI 无效或无法分排: 回退为整体估计
std::vector<int> allIdx(nTpl);
for (int i = 0; i < nTpl; i++)
{
allIdx[i] = i;
}
shiftGroups.push_back(allIdx);
shiftGroupNames.push_back("all");
}
// 2) 逐排(逐组)估计整体位移并消除
if (nCur > 0)
{
// 1) 每个模板 pin 取最近检测 pin 的位移作为候选位移(最近检测 pin 必定存在, nCur > 0)
std::vector<cv::Point2f> disp(nTpl);
for (int i = 0; i < nTpl; i++)
for (size_t g = 0; g < shiftGroups.size(); g++)
{
const std::vector<int> &group = shiftGroups[g];
const int nGroup = static_cast<int>(group.size());
if (nGroup <= 0)
{
continue;
}
// 组内相邻 pin 最小间距: 用于限制整体偏移的量级(整体偏移不应接近一个 pin 间距, 否则无法与"错配一列"区分)
float minGap = 0.0f;
for (int a = 0; a < nGroup; a++)
{
for (int b = a + 1; b < nGroup; b++)
{
float d = static_cast<float>(cv::norm(tplCenters[group[a]] - tplCenters[group[b]]));
if (minGap <= 0.0f || d < minGap)
{
minGap = d;
}
}
}
// 2.1) 组内每个模板 pin 取最近检测 pin 的位移作为候选位移(最近检测 pin 必定存在, nCur > 0)
std::vector<cv::Point2f> disp(nGroup);
for (int a = 0; a < nGroup; a++)
{
int nearestIdx = 0;
double bestD = 1e12;
for (int j = 0; j < nCur; j++)
{
double d = cv::norm(curCenters[j] - tplCenters[i]);
double d = cv::norm(curCenters[j] - tplCenters[group[a]]);
if (d < bestD)
{
bestD = d;
nearestIdx = j;
}
}
disp[i] = curCenters[nearestIdx] - tplCenters[i];
disp[a] = curCenters[nearestIdx] - tplCenters[group[a]];
}
// 2) 网格投票找主位移(峰值并列时优先取量级更小的)
// 2.2) 网格投票找主位移(峰值并列时优先取量级更小的)
const float binSize = PIN_SHIFT_BIN_SIZE_PX;
std::map<std::pair<int, int>, int> hist;
int bestCount = 0;
float bestX = 0.0f;
float bestY = 0.0f;
for (int i = 0; i < nTpl; i++)
for (int a = 0; a < nGroup; a++)
{
std::pair<int, int> key(static_cast<int>(std::lround(disp[i].x / binSize)),
static_cast<int>(std::lround(disp[i].y / binSize)));
std::pair<int, int> key(static_cast<int>(std::lround(disp[a].x / binSize)),
static_cast<int>(std::lround(disp[a].y / binSize)));
int cnt = ++hist[key];
float cx = key.first * binSize;
float cy = key.second * binSize;
@ -2408,46 +2476,47 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
}
}
// 3) 峰值邻域(±1 档)内的候选位移取均值, 同时统计支持度
// 2.3) 峰值邻域(±1 档)内的候选位移取均值, 同时统计支持度
double sumX = 0.0;
double sumY = 0.0;
int sumN = 0;
for (int i = 0; i < nTpl; i++)
for (int a = 0; a < nGroup; a++)
{
if (std::fabs(disp[i].x - bestX) <= binSize &&
std::fabs(disp[i].y - bestY) <= binSize)
if (std::fabs(disp[a].x - bestX) <= binSize &&
std::fabs(disp[a].y - bestY) <= binSize)
{
sumX += disp[i].x;
sumY += disp[i].y;
sumX += disp[a].x;
sumY += disp[a].y;
sumN++;
}
}
cv::Point2f globalShift(0.0f, 0.0f);
cv::Point2f rowShift(0.0f, 0.0f);
if (sumN > 0)
{
globalShift.x = static_cast<float>(sumX / sumN);
globalShift.y = static_cast<float>(sumY / sumN);
rowShift.x = static_cast<float>(sumX / sumN);
rowShift.y = static_cast<float>(sumY / sumN);
}
else
{
globalShift.x = bestX;
globalShift.y = bestY;
rowShift.x = bestX;
rowShift.y = bestY;
}
// 4) 支持度达标 且 位移量级小于 pin 间距的限定比例 -> 判定为整体偏移
const double supportRatio = static_cast<double>(sumN) / nTpl;
const float shiftLen = static_cast<float>(cv::norm(globalShift));
const bool bGlobalShift = (supportRatio >= PIN_SHIFT_MIN_SUPPORT_RATIO) &&
// 2.4) 支持度达标 且 位移量级小于 pin 间距的限定比例 -> 判定为该排整体偏移
const double supportRatio = static_cast<double>(sumN) / nGroup;
const float shiftLen = static_cast<float>(cv::norm(rowShift));
const bool bRowShift = (supportRatio >= PIN_SHIFT_MIN_SUPPORT_RATIO) &&
(minGap <= 0.0f || shiftLen < minGap * PIN_SHIFT_MAX_GAP_RATIO);
if (bGlobalShift)
if (bRowShift)
{
// 模板 pin 轮廓整体平移, 使其与检测 pin 一致
const int shiftX = cvRound(globalShift.x);
const int shiftY = cvRound(globalShift.y);
for (int i = 0; i < nTpl; i++)
// 该排模板 pin 轮廓整体平移, 使其与检测 pin 一致
const int shiftX = cvRound(rowShift.x);
const int shiftY = cvRound(rowShift.y);
for (int a = 0; a < nGroup; a++)
{
const int i = group[a];
for (auto &pt : m_tplPinContoursCur[i])
{
pt.x += shiftX;
@ -2458,15 +2527,18 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]);
}
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check",
"global offset removed: template pins shifted dx %.2f dy %.2f mm (%.1f %.1f px), support %d/%d",
globalShift.x * m_fImgage_Scale_X, globalShift.y * m_fImgage_Scale_Y,
globalShift.x, globalShift.y, sumN, nTpl);
"global offset removed(%s): template pins shifted dx %.2f dy %.2f mm (%.1f %.1f px), support %d/%d",
shiftGroupNames[g].c_str(),
rowShift.x * m_fImgage_Scale_X, rowShift.y * m_fImgage_Scale_Y,
rowShift.x, rowShift.y, sumN, nGroup);
}
else if (bestCount > 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Pin Check",
"global offset not applied: dx %.1f dy %.1f px, support %d/%d (ratio %.2f, pin gap %.1f px)",
globalShift.x, globalShift.y, sumN, nTpl, supportRatio, minGap);
"global offset not applied(%s): dx %.1f dy %.1f px, support %d/%d (ratio %.2f, pin gap %.1f px)",
shiftGroupNames[g].c_str(),
rowShift.x, rowShift.y, sumN, nGroup, supportRatio, minGap);
}
}
}
}

Loading…
Cancel
Save