|
|
|
|
@ -16,6 +16,12 @@
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
|
|
// ===================== 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最小间距 * 该比例
|
|
|
|
|
// ======================================================================================
|
|
|
|
|
|
|
|
|
|
// 分类映射配置文件(每行:label索引:缺陷名称;冒号后名称为该缺陷类型名称,行的顺序即缺陷类型索引)
|
|
|
|
|
static const std::string CLS_LABEL_CONFIG_FILE = "/home/aidlux/BOE/UseModel_ZB/KK_cls.txt";
|
|
|
|
|
|
|
|
|
|
@ -359,7 +365,7 @@ int ImgCheckAnalysisy::GetStatus()
|
|
|
|
|
|
|
|
|
|
std::string ImgCheckAnalysisy::GetVersion()
|
|
|
|
|
{
|
|
|
|
|
return std::string("BOE_1.3.0");
|
|
|
|
|
return std::string("BOE_1.3.1");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string ImgCheckAnalysisy::GetErrorInfo()
|
|
|
|
|
@ -820,9 +826,7 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
m_outer_roi = curOuterBound;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
// 检测PIN偏移/缺失对位置基准最敏感:PIN 位置的真实基准是产品内腔(内定位),
|
|
|
|
|
// 因此优先用内定位计算仿射(当前内边缘 -> 模板内边缘);模板/当前内定位无效时回退外定位。
|
|
|
|
|
// 注意: 这里不再对原图做 warpAffine, 只计算矩阵, 并把模板 Pin 轮廓反变换到当前图坐标系。
|
|
|
|
|
// 不对原图做 warpAffine, 只计算矩阵, 并把模板 Pin 轮廓反变换到当前图坐标系。
|
|
|
|
|
bool bUseInnerAlign = (m_tplInnerRect.size.width > 0 && m_tplInnerRect.size.height > 0 &&
|
|
|
|
|
m_inner_rroi.size.width > 0 && m_inner_rroi.size.height > 0);
|
|
|
|
|
if (bUseInnerAlign)
|
|
|
|
|
@ -2253,14 +2257,13 @@ int ImgCheckAnalysisy::Pin_Qx_Det(const cv::Mat &img)
|
|
|
|
|
Mat pin_mask;
|
|
|
|
|
int re = m_Pin_QX_Det.GetPinMask(img, &m_Edge_DetConfig, pin_mask);
|
|
|
|
|
|
|
|
|
|
// 统计tplPinMask的blob边缘点,并绘制到tplImg上
|
|
|
|
|
// 获取当前pinmask
|
|
|
|
|
vector<vector<Point>> pin_contours;
|
|
|
|
|
vector<Vec4i> hierarchy;
|
|
|
|
|
cv::findContours(pin_mask, pin_contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
|
|
|
|
//缩放比例
|
|
|
|
|
const float pin_scale_x = (pin_mask.cols > 0) ? static_cast<float>(img.cols) / pin_mask.cols : 0.0f;
|
|
|
|
|
const float pin_scale_y = (pin_mask.rows > 0) ? static_cast<float>(img.rows) / pin_mask.rows : 0.0f;
|
|
|
|
|
// 将轮廓点从 mask 坐标系 映射回 tplImg 坐标系
|
|
|
|
|
m_curPinContours.clear();
|
|
|
|
|
m_curPinContours.reserve(pin_contours.size());
|
|
|
|
|
for (size_t i = 0; i < pin_contours.size(); i++)
|
|
|
|
|
@ -2312,6 +2315,129 @@ 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;
|
|
|
|
|
for (int i = 0; i < nTpl; i++)
|
|
|
|
|
{
|
|
|
|
|
for (int j = i + 1; j < nTpl; j++)
|
|
|
|
|
{
|
|
|
|
|
float d = static_cast<float>(cv::norm(tplCenters[i] - tplCenters[j]));
|
|
|
|
|
if (minGap <= 0.0f || d < minGap)
|
|
|
|
|
{
|
|
|
|
|
minGap = d;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nCur > 0)
|
|
|
|
|
{
|
|
|
|
|
// 1) 每个模板 pin 取最近检测 pin 的位移作为候选位移(最近检测 pin 必定存在, nCur > 0)
|
|
|
|
|
std::vector<cv::Point2f> disp(nTpl);
|
|
|
|
|
for (int i = 0; i < nTpl; i++)
|
|
|
|
|
{
|
|
|
|
|
int nearestIdx = 0;
|
|
|
|
|
double bestD = 1e12;
|
|
|
|
|
for (int j = 0; j < nCur; j++)
|
|
|
|
|
{
|
|
|
|
|
double d = cv::norm(curCenters[j] - tplCenters[i]);
|
|
|
|
|
if (d < bestD)
|
|
|
|
|
{
|
|
|
|
|
bestD = d;
|
|
|
|
|
nearestIdx = j;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
disp[i] = curCenters[nearestIdx] - tplCenters[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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++)
|
|
|
|
|
{
|
|
|
|
|
std::pair<int, int> key(static_cast<int>(std::lround(disp[i].x / binSize)),
|
|
|
|
|
static_cast<int>(std::lround(disp[i].y / binSize)));
|
|
|
|
|
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 档)内的候选位移取均值, 同时统计支持度
|
|
|
|
|
double sumX = 0.0;
|
|
|
|
|
double sumY = 0.0;
|
|
|
|
|
int sumN = 0;
|
|
|
|
|
for (int i = 0; i < nTpl; i++)
|
|
|
|
|
{
|
|
|
|
|
if (std::fabs(disp[i].x - bestX) <= binSize &&
|
|
|
|
|
std::fabs(disp[i].y - bestY) <= binSize)
|
|
|
|
|
{
|
|
|
|
|
sumX += disp[i].x;
|
|
|
|
|
sumY += disp[i].y;
|
|
|
|
|
sumN++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv::Point2f globalShift(0.0f, 0.0f);
|
|
|
|
|
if (sumN > 0)
|
|
|
|
|
{
|
|
|
|
|
globalShift.x = static_cast<float>(sumX / sumN);
|
|
|
|
|
globalShift.y = static_cast<float>(sumY / sumN);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
globalShift.x = bestX;
|
|
|
|
|
globalShift.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) &&
|
|
|
|
|
(minGap <= 0.0f || shiftLen < minGap * PIN_SHIFT_MAX_GAP_RATIO);
|
|
|
|
|
|
|
|
|
|
if (bGlobalShift)
|
|
|
|
|
{
|
|
|
|
|
// 模板 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])
|
|
|
|
|
{
|
|
|
|
|
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",
|
|
|
|
|
"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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3、偏移阈值直接使用基础参数 pin_x_offset / pin_y_offset (mm),与换算为 mm 的偏移量直接比较
|
|
|
|
|
float offsetThX = 0.0f; // mm
|
|
|
|
|
float offsetThY = 0.0f; // mm
|
|
|
|
|
|