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

dev_lsy
liusiyang 2 weeks ago
parent 211296fc20
commit 050dd31240

@ -18,11 +18,24 @@
#include <fstream> #include <fstream>
// ===================== Pin 整体偏移估计参数 ===================== // ===================== 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_BIN_SIZE_PX = 4.0f; // 位移投票的网格尺寸(px), 同时作为峰值邻域半径
static const float PIN_SHIFT_MAX_GAP_RATIO = 0.5f; // 整体位移量级上限 = 相邻pin最小间距 * 该比例 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索引:缺陷名称;冒号后名称为该缺陷类型名称,行的顺序即缺陷类型索引) // 分类映射配置文件(每行:label索引:缺陷名称;冒号后名称为该缺陷类型名称,行的顺序即缺陷类型索引)
static const std::string CLS_LABEL_CONFIG_FILE = "/home/aidlux/BOE/UseModel_ZB/KK_cls.txt"; static const std::string CLS_LABEL_CONFIG_FILE = "/home/aidlux/BOE/UseModel_ZB/KK_cls.txt";
@ -2349,124 +2362,183 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
std::vector<bool> curUsed(nCur, false); std::vector<bool> curUsed(nCur, false);
// 整体偏移估计与消除(阈值见文件顶部 PIN_SHIFT_* 常量): // 整体偏移估计与消除(阈值见文件顶部 PIN_SHIFT_* 常量):
// 若足够多的模板 pin 都具有方向一致、量级接近的位移, 则把模板 pin 轮廓整体平移到与检测 pin 一致的位置,
// 后续匹配/偏移判据均基于平移后的模板 pin
{ {
// 相邻 pin 最小间距: 用于限制整体偏移的量级(整体偏移不应接近一个 pin 间距, 否则无法与"错配一列"区分) // inner 定位 ROI 转到 detImg 坐标系(detImg 由 m_outer_roi 裁剪得到)
float minGap = 0.0f; cv::RotatedRect innerRectDet = m_inner_rroi;
for (int i = 0; i < nTpl; i++) 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)
{ {
for (int j = i + 1; j < nTpl; j++) std::vector<std::vector<int>> sideIdx(2);
for (int i = 0; i < nTpl; i++)
{ {
float d = static_cast<float>(cv::norm(tplCenters[i] - tplCenters[j])); sideIdx[(PinRowSideOfRect(innerRectDet, tplCenters[i]) < 0.0f) ? 0 : 1].push_back(i);
if (minGap <= 0.0f || d < minGap) }
{ // 两排各自至少 2 个 pin 才能独立可靠估计; 否则回退为整体估计
minGap = d; if (sideIdx[0].size() >= 2 && sideIdx[1].size() >= 2)
} {
shiftGroups.push_back(sideIdx[0]);
shiftGroupNames.push_back("upper row");
shiftGroups.push_back(sideIdx[1]);
shiftGroupNames.push_back("lower row");
} }
else
{
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) if (nCur > 0)
{ {
// 1) 每个模板 pin 取最近检测 pin 的位移作为候选位移(最近检测 pin 必定存在, nCur > 0) for (size_t g = 0; g < shiftGroups.size(); g++)
std::vector<cv::Point2f> disp(nTpl);
for (int i = 0; i < nTpl; i++)
{ {
int nearestIdx = 0; const std::vector<int> &group = shiftGroups[g];
double bestD = 1e12; const int nGroup = static_cast<int>(group.size());
for (int j = 0; j < nCur; j++) if (nGroup <= 0)
{ {
double d = cv::norm(curCenters[j] - tplCenters[i]); continue;
if (d < bestD) }
// 组内相邻 pin 最小间距: 用于限制整体偏移的量级(整体偏移不应接近一个 pin 间距, 否则无法与"错配一列"区分)
float minGap = 0.0f;
for (int a = 0; a < nGroup; a++)
{
for (int b = a + 1; b < nGroup; b++)
{ {
bestD = d; float d = static_cast<float>(cv::norm(tplCenters[group[a]] - tplCenters[group[b]]));
nearestIdx = j; if (minGap <= 0.0f || d < minGap)
{
minGap = d;
}
} }
} }
disp[i] = curCenters[nearestIdx] - tplCenters[i];
}
// 2) 网格投票找主位移(峰值并列时优先取量级更小的) // 2.1) 组内每个模板 pin 取最近检测 pin 的位移作为候选位移(最近检测 pin 必定存在, nCur > 0)
const float binSize = PIN_SHIFT_BIN_SIZE_PX; std::vector<cv::Point2f> disp(nGroup);
std::map<std::pair<int, int>, int> hist; for (int a = 0; a < nGroup; a++)
int bestCount = 0; {
float bestX = 0.0f; int nearestIdx = 0;
float bestY = 0.0f; double bestD = 1e12;
for (int i = 0; i < nTpl; i++) for (int j = 0; j < nCur; j++)
{ {
std::pair<int, int> key(static_cast<int>(std::lround(disp[i].x / binSize)), double d = cv::norm(curCenters[j] - tplCenters[group[a]]);
static_cast<int>(std::lround(disp[i].y / binSize))); if (d < bestD)
int cnt = ++hist[key]; {
float cx = key.first * binSize; bestD = d;
float cy = key.second * binSize; nearestIdx = j;
if (cnt > bestCount || }
(cnt == bestCount && cx * cx + cy * cy < bestX * bestX + bestY * bestY)) }
disp[a] = curCenters[nearestIdx] - tplCenters[group[a]];
}
// 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 a = 0; a < nGroup; a++)
{ {
bestCount = cnt; std::pair<int, int> key(static_cast<int>(std::lround(disp[a].x / binSize)),
bestX = cx; static_cast<int>(std::lround(disp[a].y / binSize)));
bestY = cy; int cnt = ++hist[key];
float cx = key.first * binSize;
float cy = key.second * binSize;
if (cnt > bestCount ||
(cnt == bestCount && cx * cx + cy * cy < bestX * bestX + bestY * bestY))
{
bestCount = cnt;
bestX = cx;
bestY = cy;
}
} }
}
// 3) 峰值邻域(±1 档)内的候选位移取均值, 同时统计支持度 // 2.3) 峰值邻域(±1 档)内的候选位移取均值, 同时统计支持度
double sumX = 0.0; double sumX = 0.0;
double sumY = 0.0; double sumY = 0.0;
int sumN = 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)
{ {
sumX += disp[i].x; if (std::fabs(disp[a].x - bestX) <= binSize &&
sumY += disp[i].y; std::fabs(disp[a].y - bestY) <= binSize)
sumN++; {
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) if (sumN > 0)
{ {
globalShift.x = static_cast<float>(sumX / sumN); rowShift.x = static_cast<float>(sumX / sumN);
globalShift.y = static_cast<float>(sumY / sumN); rowShift.y = static_cast<float>(sumY / sumN);
} }
else else
{ {
globalShift.x = bestX; rowShift.x = bestX;
globalShift.y = bestY; rowShift.y = bestY;
} }
// 4) 支持度达标 且 位移量级小于 pin 间距的限定比例 -> 判定为整体偏移 // 2.4) 支持度达标 且 位移量级小于 pin 间距的限定比例 -> 判定为该排整体偏移
const double supportRatio = static_cast<double>(sumN) / nTpl; const double supportRatio = static_cast<double>(sumN) / nGroup;
const float shiftLen = static_cast<float>(cv::norm(globalShift)); const float shiftLen = static_cast<float>(cv::norm(rowShift));
const bool bGlobalShift = (supportRatio >= PIN_SHIFT_MIN_SUPPORT_RATIO) && const bool bRowShift = (supportRatio >= PIN_SHIFT_MIN_SUPPORT_RATIO) &&
(minGap <= 0.0f || shiftLen < minGap * PIN_SHIFT_MAX_GAP_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++)
{ {
for (auto &pt : m_tplPinContoursCur[i]) // 该排模板 pin 轮廓整体平移, 使其与检测 pin 一致
const int shiftX = cvRound(rowShift.x);
const int shiftY = cvRound(rowShift.y);
for (int a = 0; a < nGroup; a++)
{ {
pt.x += shiftX; const int i = group[a];
pt.y += shiftY; for (auto &pt : m_tplPinContoursCur[i])
{
pt.x += shiftX;
pt.y += shiftY;
}
// 与前面的统计口径保持一致: 中心/外包框由平移后的轮廓重新计算
tplCenters[i] = getCenter(m_tplPinContoursCur[i]);
tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]);
} }
// 与前面的统计口径保持一致: 中心/外包框由平移后的轮廓重新计算 m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check",
tplCenters[i] = getCenter(m_tplPinContoursCur[i]); "global offset removed(%s): template pins shifted dx %.2f dy %.2f mm (%.1f %.1f px), support %d/%d",
tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]); 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(%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);
} }
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);
}
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);
} }
} }
} }

Loading…
Cancel
Save