feat 添加清晰度计算,卡控模糊图

dev_lsy
liusiyang 6 days ago
parent b404e97577
commit a0f772b209

@ -98,6 +98,18 @@ struct One_Image_CheckResult_
cv::Rect CutRoi; // 当前检测图片 对应的裁切区域 cv::Rect CutRoi; // 当前检测图片 对应的裁切区域
cv::Rect Param_CropRoi; // 参数模版 对应的裁切区域 cv::Rect Param_CropRoi; // 参数模版 对应的裁切区域
std::shared_ptr<std::vector<QX_ERROR_INFO_>> pQx_ErrorList; // 缺陷错误信息 std::shared_ptr<std::vector<QX_ERROR_INFO_>> pQx_ErrorList; // 缺陷错误信息
bool bBlurNG; // 图像模糊判定: true = 检测图像模糊(判 NG)
float fBlurScore; // 图像清晰度(拉普拉斯方差), 越大越清晰
One_Image_CheckResult_()
{
CutRoi = cv::Rect(0, 0, 0, 0);
Param_CropRoi = cv::Rect(0, 0, 0, 0);
pQx_ErrorList = std::make_shared<std::vector<QX_ERROR_INFO_>>();
bBlurNG = false;
fBlurScore = 0.0f;
}
void print(std::string str = "") void print(std::string str = "")
{ {
std::cout << str << ": " std::cout << str << ": "

@ -125,6 +125,9 @@ private:
// 图片预处理 // 图片预处理
int ImgPreDet(); int ImgPreDet();
// 图像模糊度(清晰度)检测: 在 AI 检测之前执行, 返回 1 表示图像模糊(判 NG), 0 正常
int BlurCheck(const cv::Mat &img);
// 设置新的检测参数 // 设置新的检测参数
int SetNewConfig(); int SetNewConfig();

@ -623,7 +623,8 @@ int ALLImgCheckAnalysisy::SetProductResult(std::shared_ptr<Product> product)
std::shared_ptr<CheckResult> pCheckResult = pcam->ImageALLDetResultList.at(i)->result; std::shared_ptr<CheckResult> pCheckResult = pcam->ImageALLDetResultList.at(i)->result;
if (pCheckResult->qxImageResult.size() > 0) // 图像 NG(包括图像模糊在 AI 检测之前直接判定的 NG) 也要计入产品级结果
if (pCheckResult->qxImageResult.size() > 0 || pCheckResult->nresult > ERROR_TYPE_OK)
{ {
bNG = true; bNG = true;
} }

@ -18,6 +18,11 @@ Json::Value CheckResultJson::toJsonValue()
root["Param_CropRoi"]["width"] = m_pOneImgDetResult->Param_CropRoi.width; root["Param_CropRoi"]["width"] = m_pOneImgDetResult->Param_CropRoi.width;
root["Param_CropRoi"]["height"] = m_pOneImgDetResult->Param_CropRoi.height; root["Param_CropRoi"]["height"] = m_pOneImgDetResult->Param_CropRoi.height;
} }
{
// 图像模糊度(清晰度)检测结果: 在 AI 检测之前计算
root["blurScore"] = m_pOneImgDetResult->fBlurScore;
root["isBlur"] = m_pOneImgDetResult->bBlurNG;
}
for (int i = 0; i < m_pOneImgDetResult->pQx_ErrorList->size(); i++) for (int i = 0; i < m_pOneImgDetResult->pQx_ErrorList->size(); i++)
{ {
QX_ERROR_INFO_ *p = &m_pOneImgDetResult->pQx_ErrorList->at(i); QX_ERROR_INFO_ *p = &m_pOneImgDetResult->pQx_ErrorList->at(i);
@ -86,6 +91,17 @@ void CheckResultJson::toObjectFromValue(Json::Value root)
m_pOneImgDetResult->Param_CropRoi.height = root["Param_CropRoi"]["height"].asInt(); m_pOneImgDetResult->Param_CropRoi.height = root["Param_CropRoi"]["height"].asInt();
} }
} }
{
// 图像模糊度(清晰度)检测结果; 旧版本 json 可能没有该字段
if (root.isMember("blurScore"))
{
m_pOneImgDetResult->fBlurScore = root["blurScore"].asFloat();
}
if (root.isMember("isBlur"))
{
m_pOneImgDetResult->bBlurNG = root["isBlur"].asBool();
}
}
{ {
if (root.isMember("Qx")) if (root.isMember("Qx"))
{ {

@ -147,6 +147,14 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
std::shared_ptr<CheckResult> m_CheckResult_shareP = pImageResult->result; // 返回结果 std::shared_ptr<CheckResult> m_CheckResult_shareP = pImageResult->result; // 返回结果
int qxNum = pDetResult->pQx_ErrorList->size(); int qxNum = pDetResult->pQx_ErrorList->size();
// 图像模糊: 检测阶段已在 AI 检测之前判定, 这里保证结果保持 NG(复测 ReJson 读取 json 后同样生效)
if (pDetResult->bBlurNG)
{
m_CheckResult_shareP->nresult = 1;
pImageLog->AddCheckstr(PrintLevel_0, "Blur Check",
"image blurred -> NG (variance %.2f)", pDetResult->fBlurScore);
}
// map<string, vector<iregionQxZoningInfo>> allQxZoning; // map<string, vector<iregionQxZoningInfo>> allQxZoning;
map<string, vector<singleQxZoningInfo>> allQxZoning; map<string, vector<singleQxZoningInfo>> allQxZoning;

@ -23,6 +23,11 @@ static const float PIN_SHIFT_BIN_SIZE_PX = 4.0f; // 位移投票的网格
static const float PIN_SHIFT_MAX_GAP_RATIO = 0.8f; // 整体位移量级上限 = 相邻pin最小间距 * 该比例 static const float PIN_SHIFT_MAX_GAP_RATIO = 0.8f; // 整体位移量级上限 = 相邻pin最小间距 * 该比例
// ====================================================================================== // ======================================================================================
// ===================== 图像模糊度(清晰度)检测参数 =====================
// 计算清晰度前把检测图最长边缩放到该尺寸(只缩小不放大), 使阈值与图像分辨率无关
static const int BLUR_CHECK_MAX_SIZE = 1000;
// ======================================================================
// 计算点相对旋转矩形“长边轴线”的带符号法向距离(检测图坐标系): // 计算点相对旋转矩形“长边轴线”的带符号法向距离(检测图坐标系):
// 取矩形长边方向作为 Pin 排方向, 返回点沿短边(法线)方向相对矩形中心的投影; // 取矩形长边方向作为 Pin 排方向, 返回点沿短边(法线)方向相对矩形中心的投影;
// 正负号天然把分布在矩形两侧的 Pin 分成两组(上下两排分别估计整体偏移)。 // 正负号天然把分布在矩形两侧的 Pin 分成两组(上下两排分别估计整体偏移)。
@ -804,6 +809,98 @@ int ImgCheckAnalysisy::TransformTplPinContoursToCur()
return 0; return 0;
} }
// 图像模糊度(清晰度)检测: 必须在 AI 检测之前执行。
// 1) 检测图 detImg 转灰度(本身就是单通道, 这里做兼容);
// 2) 最长边缩放到 BLUR_CHECK_MAX_SIZE(只缩小不放大), 保证阈值与图像分辨率无关;
// 3) 用拉普拉斯算子(CV_64F)响应的方差作为清晰度指标: 方差越小越模糊;
// 4) 方差 < 阈值(基础参数 blurDet.fThreshold, 对应界面参数 blur_threshold) -> 判为模糊图片,
// 参数 blurDet.bNG(界面 bNG)打开时直接判 NG。
// 返回: 1 图像模糊且需要判 NG; 0 正常 / 功能未开启 / 图像无效
int ImgCheckAnalysisy::BlurCheck(const cv::Mat &img)
{
if (m_pbaseCheckFunction == NULL || !m_pbaseCheckFunction->blurDet.bOpen ||
m_pbaseCheckFunction->blurDet.fThreshold <= 0.0f)
{
return 0;
}
// 每帧重置模糊判定结果
if (m_pDetResult)
{
m_pDetResult->bBlurNG = false;
m_pDetResult->fBlurScore = 0.0f;
}
if (img.empty() || img.cols < 5 || img.rows < 5)
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Blur Check", "input img invalid, skip");
return 0;
}
// 1、转灰度(detImg 为单通道, 其他通道情况做兼容)
cv::Mat grayImg;
if (1 == img.channels())
{
grayImg = img;
}
else if (3 == img.channels())
{
cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY);
}
else if (4 == img.channels())
{
cv::cvtColor(img, grayImg, cv::COLOR_BGRA2GRAY);
}
else
{
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Blur Check", "unsupported channels %d, skip", img.channels());
return 0;
}
// 2、按最长边缩放(只缩小不放大)
cv::Mat checkImg = grayImg;
const int nMaxSide = std::max(grayImg.cols, grayImg.rows);
if (nMaxSide > BLUR_CHECK_MAX_SIZE)
{
const double fScale = static_cast<double>(BLUR_CHECK_MAX_SIZE) / static_cast<double>(nMaxSide);
cv::resize(grayImg, checkImg, cv::Size(), fScale, fScale, cv::INTER_AREA);
}
// 3、拉普拉斯方差(越大越清晰)
cv::Mat lapImg;
cv::Laplacian(checkImg, lapImg, CV_64F, 3);
cv::Scalar meanVal;
cv::Scalar stdVal;
cv::meanStdDev(lapImg, meanVal, stdVal);
const float fScore = static_cast<float>(stdVal[0] * stdVal[0]);
const float fThreshold = m_pbaseCheckFunction->blurDet.fThreshold;
const bool bNGOpen = m_pbaseCheckFunction->blurDet.bNG;
const bool bBlur = (fScore < fThreshold);
if (m_pDetResult)
{
m_pDetResult->fBlurScore = fScore;
m_pDetResult->bBlurNG = (bBlur && bNGOpen);
}
m_pdetlog->AddCheckstr(PrintLevel_0, "Blur Check",
"variance %.2f %s threshold %.2f -> %s (bNG %d, img [%d %d] -> check [%d %d])",
fScore, bBlur ? "<" : ">=", fThreshold, bBlur ? "BLUR" : "OK",
static_cast<int>(bNGOpen),
grayImg.cols, grayImg.rows, checkImg.cols, checkImg.rows);
if (bBlur && bNGOpen)
{
m_pdetlog->AddCheckstr(PrintLevel_0, "Blur Check", "image blurred -> judge NG");
return 1;
}
if (bBlur)
{
m_pdetlog->AddCheckstr(PrintLevel_0, "Blur Check", "image blurred, but bNG is off -> not NG");
}
return 0;
}
int ImgCheckAnalysisy::CheckRun() int ImgCheckAnalysisy::CheckRun()
{ {
@ -1064,6 +1161,15 @@ int ImgCheckAnalysisy::CheckRun()
{ {
m_pImageAllResult->detImg = image(m_outer_roi).clone(); m_pImageAllResult->detImg = image(m_outer_roi).clone();
} }
/*图像模糊度(清晰度)检测: 必须在 AI 检测之前完成, 模糊图片直接判 NG*/
long time_blur_s = CheckUtil::getcurTime();
const bool bBlurNG = (BlurCheck(m_pImageAllResult->detImg) == 1);
long time_blur_e = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_0, "2.2、pre detect",
"-------------------------Blur Check------------%s---time %ld----\n",
bBlurNG ? "NG" : "OK", time_blur_e - time_blur_s);
/*多线程开启 AI 推理检测*/ /*多线程开启 AI 推理检测*/
long time_AI_s = CheckUtil::getcurTime(); long time_AI_s = CheckUtil::getcurTime();
m_AItask = std::make_shared<TaskInfo>(); m_AItask = std::make_shared<TaskInfo>();
@ -1086,6 +1192,12 @@ int ImgCheckAnalysisy::CheckRun()
m_CheckResult_shareP->nresult = 0; m_CheckResult_shareP->nresult = 0;
// 图像模糊: 在 AI 检测之前已判定, 直接判 NG(nresult=1 与区域参数判断出的 NG 语义一致)
if (bBlurNG)
{
m_CheckResult_shareP->nresult = 1;
}
/*把临时可以绘制的结果都绘制出来。*/ /*把临时可以绘制的结果都绘制出来。*/
DrawResult_Step_1(); DrawResult_Step_1();
long time_AI_e; long time_AI_e;

@ -1187,6 +1187,46 @@ struct Base_Function_Merge_QueXi
}; };
// 基础检测功能 图像模糊度(清晰度)检测
// 执行时机: AI 残点检测之前, 对送检图(检测图 detImg)计算模糊度, 模糊图片可直接判 NG
// 界面参数: itemCode = "Blur_Detect", form.Blur_Config = { bNG, blur_threshold }
struct Base_Function_Blur
{
bool bOpen; // 是否开启
bool bNG; // 模糊图片是否直接判 NG
float fThreshold; // 模糊度阈值(拉普拉斯方差下限): 方差低于该值判为模糊
Base_Function_Blur()
{
Init();
}
void Init()
{
bOpen = true; // 默认开启(参数里没有该配置项时也做模糊判定)
bNG = true; // 默认模糊即 NG
fThreshold = 800.0f; // 默认阈值(实测: 清晰图 ~8100, sigma=3 模糊 ~880, sigma=5 模糊 ~250)
}
void copy(Base_Function_Blur tem)
{
this->bOpen = tem.bOpen;
this->bNG = tem.bNG;
this->fThreshold = tem.fThreshold;
}
void print(std::string str)
{
printf("%s>>bOpen %d bNG %d fThreshold %f \n", str.c_str(), bOpen, bNG, fThreshold);
}
std::string GetInfo(std::string str)
{
char buffer[256];
sprintf(buffer, "%s>>bOpen %d bNG %d fThreshold %f \n", str.c_str(), bOpen, bNG, fThreshold);
std::string str123 = buffer;
return str123;
}
};
// 基础检测功能 大缺陷 NG // 基础检测功能 大缺陷 NG
struct Base_Function_BigNG struct Base_Function_BigNG
{ {
@ -1295,6 +1335,7 @@ struct BaseCheckFunction
Base_Function_Pin_Det pinDet; Base_Function_Pin_Det pinDet;
Base_Function_SaveImg saveImg; Base_Function_SaveImg saveImg;
Base_Function_Merge_QueXi Merge_QueXi; Base_Function_Merge_QueXi Merge_QueXi;
Base_Function_Blur blurDet;
Base_Function_BigNG bigNG; Base_Function_BigNG bigNG;
BaseCheckFunction() BaseCheckFunction()
{ {
@ -1306,6 +1347,7 @@ struct BaseCheckFunction
pinDet.Init(); pinDet.Init();
saveImg.Init(); saveImg.Init();
Merge_QueXi.Init(); Merge_QueXi.Init();
blurDet.Init();
bigNG.Init(); bigNG.Init();
} }
void copy(BaseCheckFunction tem) void copy(BaseCheckFunction tem)
@ -1314,6 +1356,7 @@ struct BaseCheckFunction
this->pinDet.copy(tem.pinDet); this->pinDet.copy(tem.pinDet);
this->saveImg.copy(tem.saveImg); this->saveImg.copy(tem.saveImg);
this->Merge_QueXi.copy(tem.Merge_QueXi); this->Merge_QueXi.copy(tem.Merge_QueXi);
this->blurDet.copy(tem.blurDet);
this->bigNG.copy(tem.bigNG); this->bigNG.copy(tem.bigNG);
} }
void print(std::string str) void print(std::string str)
@ -1323,6 +1366,7 @@ struct BaseCheckFunction
pinDet.print("pinDet"); pinDet.print("pinDet");
saveImg.print("saveImg"); saveImg.print("saveImg");
Merge_QueXi.print("Merge_QueXi"); Merge_QueXi.print("Merge_QueXi");
blurDet.print("blurDet");
bigNG.print("bigNG"); bigNG.print("bigNG");
} }
std::string GetInfo(std::string str) std::string GetInfo(std::string str)
@ -1332,6 +1376,7 @@ struct BaseCheckFunction
str123 += pinDet.GetInfo("pinDet"); str123 += pinDet.GetInfo("pinDet");
str123 += saveImg.GetInfo("saveImg"); str123 += saveImg.GetInfo("saveImg");
str123 += Merge_QueXi.GetInfo("Merge_QueXi"); str123 += Merge_QueXi.GetInfo("Merge_QueXi");
str123 += blurDet.GetInfo("blurDet");
str123 += bigNG.GetInfo("bigNG"); str123 += bigNG.GetInfo("bigNG");
// str123 += "\n"; // str123 += "\n";
return str123; return str123;

@ -887,6 +887,43 @@ int BaseFuntonConfigJson::GetFunction(Json::Value value)
_config.Merge_QueXi.bOpen = value_f["isOpen"].asBool(); _config.Merge_QueXi.bOpen = value_f["isOpen"].asBool();
} }
// 图像模糊度(清晰度)检测: AI 检测之前执行, 模糊图片可判 NG
if ("Blur_Detect" == strCode)
{
auto value_f = value;
// std::cout << value_f << std::endl;
// getchar();
_config.blurDet.bOpen = value_f["isOpen"].asBool();
if (_config.blurDet.bOpen)
{
if (value_f["form"]["Blur_Config"]["bNG"])
{
_config.blurDet.bNG = value_f["form"]["Blur_Config"]["bNG"].asBool();
}
// 模糊度阈值: blur_threshold(界面) / threshold 兼容; 未配置或 <= 0 时保留默认阈值
float fThreshold = 0.0f;
if (value_f["form"]["Blur_Config"]["blur_threshold"])
{
fThreshold = value_f["form"]["Blur_Config"]["blur_threshold"].asFloat();
}
else if (value_f["form"]["Blur_Config"]["threshold"])
{
fThreshold = value_f["form"]["Blur_Config"]["threshold"].asFloat();
}
if (fThreshold > 0.0f)
{
_config.blurDet.fThreshold = fThreshold;
}
}
else
{
// 关闭: 阈值置 0, 不做模糊判定
_config.blurDet.bOpen = false;
_config.blurDet.fThreshold = 0.0f;
}
// _config.blurDet.print("blurDet");
}
if ("Big_NG" == strCode) if ("Big_NG" == strCode)
{ {
auto value_f = value; auto value_f = value;

Loading…
Cancel
Save