|
|
|
@ -1145,6 +1145,247 @@ int ImageResultJudge::FilterMsBlindNG(std::shared_ptr<CameraResult> pCheck_Resul
|
|
|
|
return nFilterNum;
|
|
|
|
return nFilterNum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* @brief 跨通道同位置判断:判断当前缺陷(产品坐标系ROI)在过滤通道是否存在指定类型的同位置缺陷
|
|
|
|
|
|
|
|
* 同位置判断方式参考暗点联合分析:当前ROI与过滤通道缺陷ROI双方均按 expand 扩张后计算 IOU
|
|
|
|
|
|
|
|
* @param imgResults 当前相机所有通道的检测结果
|
|
|
|
|
|
|
|
* @param cur_channel_name 当前缺陷所属通道(主通道)
|
|
|
|
|
|
|
|
* @param filter_channels 需要比较的过滤通道
|
|
|
|
|
|
|
|
* @param match_qx_types 过滤通道中参与比较的缺陷类型(CONFIG_QX_NAME_)
|
|
|
|
|
|
|
|
* @param qx_product_roi 当前缺陷的产品坐标系ROI
|
|
|
|
|
|
|
|
* @param roi_expand 扩张像素
|
|
|
|
|
|
|
|
* @param iou_threshold IOU阈值
|
|
|
|
|
|
|
|
* @param strTag 日志标签
|
|
|
|
|
|
|
|
* @param plog 日志
|
|
|
|
|
|
|
|
* @return bool 过滤通道同位置存在指定类型缺陷返回true
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool ImageResultJudge::IsOverlapQxInFilterChannels(const std::vector<std::shared_ptr<ImageAllResult>> &imgResults,
|
|
|
|
|
|
|
|
const std::string &cur_channel_name,
|
|
|
|
|
|
|
|
const std::vector<std::string> &filter_channels,
|
|
|
|
|
|
|
|
const std::vector<int> &match_qx_types,
|
|
|
|
|
|
|
|
const cv::Rect &qx_product_roi,
|
|
|
|
|
|
|
|
int roi_expand, float iou_threshold,
|
|
|
|
|
|
|
|
const std::string &strTag,
|
|
|
|
|
|
|
|
std::shared_ptr<DetLog> plog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 防御:ROI非法/未配置过滤通道/未指定匹配类型时不判断,避免无效计算与除零
|
|
|
|
|
|
|
|
if (qx_product_roi.width <= 0 || qx_product_roi.height <= 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filter_channels.empty() || match_qx_types.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pAnalysisyConfig == nullptr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm;
|
|
|
|
|
|
|
|
int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 与暗点联合分析保持一致:双方均按 expand 扩张后再计算 IOU
|
|
|
|
|
|
|
|
cv::Rect roi_expanded(qx_product_roi.x - roi_expand, qx_product_roi.y - roi_expand,
|
|
|
|
|
|
|
|
qx_product_roi.width + roi_expand * 2, qx_product_roi.height + roi_expand * 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int imgidx = 0; imgidx < (int)imgResults.size(); imgidx++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::shared_ptr<ImageAllResult> pFilterImage = imgResults.at(imgidx);
|
|
|
|
|
|
|
|
if (pFilterImage == nullptr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 只与配置的过滤通道比较;当前通道不参与,避免自身重叠导致误过滤
|
|
|
|
|
|
|
|
if (pFilterImage->strChannel == cur_channel_name)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::find(filter_channels.begin(), filter_channels.end(), pFilterImage->strChannel) == filter_channels.end())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<One_Image_CheckResult_> pFilterDetResult = pFilterImage->pDetResult;
|
|
|
|
|
|
|
|
if (pFilterDetResult == nullptr || pFilterDetResult->pQx_ErrorList == nullptr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 只统计过滤通道内指定类型的缺陷,与具体判定结果无关
|
|
|
|
|
|
|
|
for (int j = 0; j < (int)pFilterDetResult->pQx_ErrorList->size(); j++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const QX_ERROR_INFO_ &filter_qx = pFilterDetResult->pQx_ErrorList->at(j);
|
|
|
|
|
|
|
|
if (std::find(match_qx_types.begin(), match_qx_types.end(), filter_qx.nconfig_qx_type) == match_qx_types.end())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::Rect filter_roi = filter_qx.roi;
|
|
|
|
|
|
|
|
filter_roi.x += fproduct_offx_piexl;
|
|
|
|
|
|
|
|
cv::Rect filter_expanded(filter_roi.x - roi_expand, filter_roi.y - roi_expand,
|
|
|
|
|
|
|
|
filter_roi.width + roi_expand * 2, filter_roi.height + roi_expand * 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float fiou = CheckUtil::CalIoU(roi_expanded, filter_expanded);
|
|
|
|
|
|
|
|
if (plog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
plog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, strTag,
|
|
|
|
|
|
|
|
"main channel %s roi %s <-> filter channel %s qx %d roi %s expand %d iou %f",
|
|
|
|
|
|
|
|
cur_channel_name.c_str(), CheckUtil::GetRectString(qx_product_roi).c_str(),
|
|
|
|
|
|
|
|
pFilterImage->strChannel.c_str(), j,
|
|
|
|
|
|
|
|
CheckUtil::GetRectString(filter_roi).c_str(), roi_expand, fiou);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// IOU 为 NaN(ROI 非法导致的除零)时比较结果为 false,不会误过滤
|
|
|
|
|
|
|
|
if (fiou > iou_threshold)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (plog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
plog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strTag,
|
|
|
|
|
|
|
|
"main channel %s roi %s overlap filter channel %s qx %d roi %s expand %d iou %f > %f",
|
|
|
|
|
|
|
|
cur_channel_name.c_str(), CheckUtil::GetRectString(qx_product_roi).c_str(),
|
|
|
|
|
|
|
|
pFilterImage->strChannel.c_str(), j,
|
|
|
|
|
|
|
|
CheckUtil::GetRectString(filter_roi).c_str(), roi_expand, fiou, iou_threshold);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* @brief 跨通道联合判断(通用):主通道中指定类型的NG缺陷,若在过滤通道同位置存在指定类型的缺陷,则撤销该NG
|
|
|
|
|
|
|
|
* 同位置判断方式参考暗点联合分析:双方按 expand 扩张后计算 IOU, 大于 iou_threshold 认为同位置
|
|
|
|
|
|
|
|
* @param pCheck_Result 当前相机检测结果(包含该相机所有通道的检测结果)
|
|
|
|
|
|
|
|
* @param main_channels 主通道
|
|
|
|
|
|
|
|
* @param filter_channels 过滤通道
|
|
|
|
|
|
|
|
* @param main_qx_types 参与判断的主通道缺陷类型(CONFIG_QX_NAME_)
|
|
|
|
|
|
|
|
* @param match_qx_types 过滤通道中参与比较的缺陷类型(CONFIG_QX_NAME_)
|
|
|
|
|
|
|
|
* @param roi_expand 扩张像素
|
|
|
|
|
|
|
|
* @param iou_threshold IOU阈值
|
|
|
|
|
|
|
|
* @param strTag 日志标签
|
|
|
|
|
|
|
|
* @param strReason 过滤原因(日志)
|
|
|
|
|
|
|
|
* @return int 被过滤的NG个数
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int ImageResultJudge::FilterCrossChannelNG(std::shared_ptr<CameraResult> pCheck_Result,
|
|
|
|
|
|
|
|
const std::vector<std::string> &main_channels,
|
|
|
|
|
|
|
|
const std::vector<std::string> &filter_channels,
|
|
|
|
|
|
|
|
const std::vector<int> &main_qx_types,
|
|
|
|
|
|
|
|
const std::vector<int> &match_qx_types,
|
|
|
|
|
|
|
|
int roi_expand, float iou_threshold,
|
|
|
|
|
|
|
|
const std::string &strTag,
|
|
|
|
|
|
|
|
const std::string &strReason)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (pCheck_Result == nullptr || m_pAnalysisyConfig == nullptr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 未配置主通道/过滤通道/判断类型时不需要判断
|
|
|
|
|
|
|
|
if (main_channels.empty() || filter_channels.empty() || main_qx_types.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
float fproduct_offx_mm = m_pAnalysisyConfig->commonCheckConfig.baseConfig.fProduct_Off_X_mm;
|
|
|
|
|
|
|
|
int fproduct_offx_piexl = int(fproduct_offx_mm / m_fImgage_Scale_X);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int nFilterNum = 0;
|
|
|
|
|
|
|
|
for (int imgidx = 0; imgidx < (int)pCheck_Result->ImageALLDetResultList.size(); imgidx++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::shared_ptr<ImageAllResult> pImageResult = pCheck_Result->ImageALLDetResultList.at(imgidx);
|
|
|
|
|
|
|
|
if (pImageResult == nullptr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 只处理配置的主通道
|
|
|
|
|
|
|
|
if (std::find(main_channels.begin(), main_channels.end(), pImageResult->strChannel) == main_channels.end())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<One_Image_CheckResult_> pDetResult = pImageResult->pDetResult; // 检测结果
|
|
|
|
|
|
|
|
if (pDetResult == nullptr || pDetResult->pQx_ErrorList == nullptr || pImageResult->result == nullptr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<DetLog> pImageLog = pImageResult->detlog;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int qxidx = 0; qxidx < (int)pDetResult->pQx_ErrorList->size(); qxidx++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
QX_ERROR_INFO_ *QX_info = &pDetResult->pQx_ErrorList->at(qxidx);
|
|
|
|
|
|
|
|
// 只处理指定类型的 NG 结果(YS 不降级)
|
|
|
|
|
|
|
|
if (QX_info->result != QX_RESULT_TYPE_NG)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::find(main_qx_types.begin(), main_qx_types.end(), QX_info->nconfig_qx_type) == main_qx_types.end())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Rect product_roi = QX_info->roi;
|
|
|
|
|
|
|
|
product_roi.x += fproduct_offx_piexl;
|
|
|
|
|
|
|
|
// 过滤通道同位置不存在匹配类型的缺陷时,保留该NG
|
|
|
|
|
|
|
|
if (!IsOverlapQxInFilterChannels(pCheck_Result->ImageALLDetResultList,
|
|
|
|
|
|
|
|
pImageResult->strChannel,
|
|
|
|
|
|
|
|
filter_channels, match_qx_types,
|
|
|
|
|
|
|
|
product_roi,
|
|
|
|
|
|
|
|
roi_expand, iou_threshold,
|
|
|
|
|
|
|
|
strTag, pImageLog))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 撤销NG结果
|
|
|
|
|
|
|
|
UndoNGResult(pImageResult, qxidx, QX_info);
|
|
|
|
|
|
|
|
nFilterNum++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pImageLog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pImageLog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, strTag,
|
|
|
|
|
|
|
|
"img %s cam %s blob %d qx %s roi %s : %s",
|
|
|
|
|
|
|
|
pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(),
|
|
|
|
|
|
|
|
qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str(),
|
|
|
|
|
|
|
|
strReason.c_str());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_pQX_Merge_Analysis != nullptr && m_pQX_Merge_Analysis->m_pMergedetlog)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_pQX_Merge_Analysis->m_pMergedetlog->AddCheckstr(PrintLevel_1, 3, strTag,
|
|
|
|
|
|
|
|
"img %s cam %s blob %d qx %s roi %s : %s",
|
|
|
|
|
|
|
|
pImageResult->strChannel.c_str(), pImageResult->cameraBaseResult->strCameraName.c_str(),
|
|
|
|
|
|
|
|
qxidx, QX_info->qx_name.c_str(), CheckUtil::GetRectString(product_roi).c_str(),
|
|
|
|
|
|
|
|
strReason.c_str());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nFilterNum;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* @brief cell黑白点联合判断:主通道(cellbw_main_channel)检出的cell黑白点在过滤通道(cellbw_filter_channels)
|
|
|
|
|
|
|
|
* 同位置存在cell黑白点(白点/黑点均算)时不算cell黑白点(撤销NG)
|
|
|
|
|
|
|
|
* 同位置判断方式参考暗点联合分析:双方按 cellbw_expand 扩张后计算 IOU, 大于 cellbw_iou 认为同位置
|
|
|
|
|
|
|
|
* @param pCheck_Result 当前相机检测结果(包含该相机所有通道的检测结果)
|
|
|
|
|
|
|
|
* @return int 被过滤的cell黑白点NG个数
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int ImageResultJudge::FilterCellBwNG(std::shared_ptr<CameraResult> pCheck_Result)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (m_pbaseCheckFunction == nullptr)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Base_Function_CellBW_Check *pCellBwParam = &m_pbaseCheckFunction->cellbw_check;
|
|
|
|
|
|
|
|
// 未开启cell黑白点分析时不需要判断(主通道/过滤通道为空时由通用逻辑直接返回)
|
|
|
|
|
|
|
|
if (!pCellBwParam->bOpen)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 白点/黑点通用:主通道与过滤通道均按 cell 黑白点匹配(白↔白、黑↔黑、白↔黑都算)
|
|
|
|
|
|
|
|
const std::vector<int> qxTypes = {CONFIG_QX_NAME_white_Cell, CONFIG_QX_NAME_black_Cell};
|
|
|
|
|
|
|
|
return FilterCrossChannelNG(pCheck_Result,
|
|
|
|
|
|
|
|
pCellBwParam->cellbw_main_channel, pCellBwParam->cellbw_filter_channels,
|
|
|
|
|
|
|
|
qxTypes, qxTypes,
|
|
|
|
|
|
|
|
(int)(round)(pCellBwParam->CellBW_Expand), pCellBwParam->CellBW_IOU,
|
|
|
|
|
|
|
|
"CellBw", "过滤通道同位置存在cell黑白点, 不算cell黑白点");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult, QX_Analysis_Result_List *ptemre)
|
|
|
|
int ImageResultJudge::MergeResult(std::shared_ptr<ImageAllResult> pImageResult, QX_Analysis_Result_List *ptemre)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|