|
|
|
@ -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,53 +2362,108 @@ 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;
|
|
|
|
|
|
|
|
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 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]));
|
|
|
|
shiftGroups.push_back(sideIdx[0]);
|
|
|
|
if (minGap <= 0.0f || d < minGap)
|
|
|
|
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)
|
|
|
|
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++)
|
|
|
|
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;
|
|
|
|
int nearestIdx = 0;
|
|
|
|
double bestD = 1e12;
|
|
|
|
double bestD = 1e12;
|
|
|
|
for (int j = 0; j < nCur; j++)
|
|
|
|
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)
|
|
|
|
if (d < bestD)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
bestD = d;
|
|
|
|
bestD = d;
|
|
|
|
nearestIdx = j;
|
|
|
|
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;
|
|
|
|
const float binSize = PIN_SHIFT_BIN_SIZE_PX;
|
|
|
|
std::map<std::pair<int, int>, int> hist;
|
|
|
|
std::map<std::pair<int, int>, int> hist;
|
|
|
|
int bestCount = 0;
|
|
|
|
int bestCount = 0;
|
|
|
|
float bestX = 0.0f;
|
|
|
|
float bestX = 0.0f;
|
|
|
|
float bestY = 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)),
|
|
|
|
std::pair<int, int> key(static_cast<int>(std::lround(disp[a].x / binSize)),
|
|
|
|
static_cast<int>(std::lround(disp[i].y / binSize)));
|
|
|
|
static_cast<int>(std::lround(disp[a].y / binSize)));
|
|
|
|
int cnt = ++hist[key];
|
|
|
|
int cnt = ++hist[key];
|
|
|
|
float cx = key.first * binSize;
|
|
|
|
float cx = key.first * binSize;
|
|
|
|
float cy = key.second * 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 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 &&
|
|
|
|
if (std::fabs(disp[a].x - bestX) <= binSize &&
|
|
|
|
std::fabs(disp[i].y - bestY) <= binSize)
|
|
|
|
std::fabs(disp[a].y - bestY) <= binSize)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
sumX += disp[i].x;
|
|
|
|
sumX += disp[a].x;
|
|
|
|
sumY += disp[i].y;
|
|
|
|
sumY += disp[a].y;
|
|
|
|
sumN++;
|
|
|
|
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 一致
|
|
|
|
// 该排模板 pin 轮廓整体平移, 使其与检测 pin 一致
|
|
|
|
const int shiftX = cvRound(globalShift.x);
|
|
|
|
const int shiftX = cvRound(rowShift.x);
|
|
|
|
const int shiftY = cvRound(globalShift.y);
|
|
|
|
const int shiftY = cvRound(rowShift.y);
|
|
|
|
for (int i = 0; i < nTpl; i++)
|
|
|
|
for (int a = 0; a < nGroup; a++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
const int i = group[a];
|
|
|
|
for (auto &pt : m_tplPinContoursCur[i])
|
|
|
|
for (auto &pt : m_tplPinContoursCur[i])
|
|
|
|
{
|
|
|
|
{
|
|
|
|
pt.x += shiftX;
|
|
|
|
pt.x += shiftX;
|
|
|
|
@ -2458,15 +2527,18 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
|
|
|
|
tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]);
|
|
|
|
tplRects[i] = cv::boundingRect(m_tplPinContoursCur[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check",
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "Pin Check",
|
|
|
|
"global offset removed: template pins shifted dx %.2f dy %.2f mm (%.1f %.1f px), support %d/%d",
|
|
|
|
"global offset removed(%s): 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,
|
|
|
|
shiftGroupNames[g].c_str(),
|
|
|
|
globalShift.x, globalShift.y, sumN, nTpl);
|
|
|
|
rowShift.x * m_fImgage_Scale_X, rowShift.y * m_fImgage_Scale_Y,
|
|
|
|
|
|
|
|
rowShift.x, rowShift.y, sumN, nGroup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (bestCount > 0)
|
|
|
|
else if (bestCount > 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Pin Check",
|
|
|
|
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)",
|
|
|
|
"global offset not applied(%s): dx %.1f dy %.1f px, support %d/%d (ratio %.2f, pin gap %.1f px)",
|
|
|
|
globalShift.x, globalShift.y, sumN, nTpl, supportRatio, minGap);
|
|
|
|
shiftGroupNames[g].c_str(),
|
|
|
|
|
|
|
|
rowShift.x, rowShift.y, sumN, nGroup, supportRatio, minGap);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|