diff --git a/AlgorithmModule/include/ImageResultJudge.h b/AlgorithmModule/include/ImageResultJudge.h index b06697c..90b0c7c 100644 --- a/AlgorithmModule/include/ImageResultJudge.h +++ b/AlgorithmModule/include/ImageResultJudge.h @@ -29,6 +29,7 @@ private: Draw_rect, Draw_circle, Draw_str, + Draw_lines, }; struct txtDrawBackgroundk { @@ -91,6 +92,7 @@ private: int dr = 0; float txtsize = 0.8; std::shared_ptr txtbackgroundk; + std::vector linePts; // 连线端点列表(用于 Draw_lines) }; struct singleQxInfo{ @@ -104,6 +106,7 @@ private: float scale_Y; float judge_dis; int judge_num; + int param_group_id; // 参数组唯一标识,用于区分不同参数组的缺陷集合 Point center; vector qxList; void Init(){ @@ -111,6 +114,7 @@ private: scale_Y = 0; judge_dis = 0; judge_num = 0; + param_group_id = -1; center = Point(0, 0); qxList.clear(); } @@ -131,7 +135,10 @@ private: { if(calulateDisSquared(qxInfo.pcenter) < judge_dis * judge_dis) { - center = Point((qxInfo.pcenter.x + center.x) / 2, (qxInfo.pcenter.y + center.y) / 2); + // 质心更新:所有点等权重的算术平均 + int n = (int)qxList.size() + 1; + center.x = (int)(((int64_t)center.x * (n - 1) + qxInfo.pcenter.x) / n); + center.y = (int)(((int64_t)center.y * (n - 1) + qxInfo.pcenter.y) / n); qxList.push_back(qxInfo); return true; } @@ -152,6 +159,12 @@ private: struct iregionQxZoningInfo{ vector ictQxZoningList; }; + // 用于 DrawResult 的分组连线信息 + struct QxZoningDrawGroup{ + bool isNG; // true=NG组, false=YS组 + std::vector centers; // 组内各缺陷中心点(detection坐标系,绘制时需乘缩放系数) + }; + public: ImageResultJudge(/* args */); ~ImageResultJudge(); @@ -172,6 +185,9 @@ private: cv::Rect GetCutRoi(cv::Rect &roi, const cv::Mat &img); // 获取缺陷 对应的AI输入 输出图片, int GetAIDetImg(std::shared_ptr pImageResult, cv::Point pcenter, cv::Mat &AI_InImg, cv::Mat &AI_OutImg); + // 构建缺陷图片结果(裁剪、缩放、填充字段、获取AI图片),返回完整的 QXImageResult + QXImageResult BuildDefectImage(std::shared_ptr pImageResult, QX_ERROR_INFO_ *QX_info, + int qxidx, cv::Point pCenter, float fs_resize_x, float fs_resize_y); int sendTask(std::shared_ptr task); @@ -203,6 +219,9 @@ private: std::condition_variable m_task_cv_; std::shared_ptr ptr_thread_Draw; std::condition_variable m_drawComplate_cv_; + + // 分组连线信息(ResultJudge 填充,DrawResult 消费) + std::vector m_zoningDrawGroups; }; #endif \ No newline at end of file diff --git a/AlgorithmModule/src/ImageResultJudge.cpp b/AlgorithmModule/src/ImageResultJudge.cpp index 679b186..4d32cb8 100644 --- a/AlgorithmModule/src/ImageResultJudge.cpp +++ b/AlgorithmModule/src/ImageResultJudge.cpp @@ -116,6 +116,68 @@ int ImageResultJudge::GetAIDetImg(std::shared_ptr pImageResult, } return 0; } +QXImageResult ImageResultJudge::BuildDefectImage(std::shared_ptr pImageResult, QX_ERROR_INFO_ *QX_info, + int qxidx, cv::Point pCenter, float fs_resize_x, float fs_resize_y) +{ + cv::Rect roi = QX_info->roi; + float JudgArea = QX_info->JudgArea; + float flen = QX_info->flen; + float fbreadth = QX_info->fbreadth; + float grayDis = QX_info->grayDis; + int energy = QX_info->energy; + int maxValue = QX_info->maxValue; + int config_qx_type = QX_info->nconfig_qx_type; + + QXImageResult tem; + tem.idx = qxidx; + cv::Rect CutRoi = GetCutRoi(roi, pImageResult->detImg); + cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT); + + if (pImageResult->detImg.channels() == 1) + { + cv::cvtColor(pImageResult->detImg(CutRoi), tem.srcImg, cv::COLOR_GRAY2BGR); + } + else + { + tem.srcImg = pImageResult->detImg(CutRoi).clone(); + } + cv::resize(tem.srcImg, tem.resizeImg, sz); + + int nqx_type = ConfigTypeToResultType(config_qx_type); + tem.type = nqx_type; + tem.area = JudgArea; + tem.energy = energy; + tem.hj = grayDis; + tem.max_v = maxValue; + tem.strTypeName = QX_Result_Names[nqx_type]; + tem.qx_Code = QX_Result_Code[nqx_type]; + tem.srcImgroi = roi; + tem.len = flen; + tem.breadth = fbreadth; + tem.qx_type = 0; + tem.fScore = 0; + tem.density = 0; + + tem.resizeImgroi.x = roi.x * fs_resize_x; + tem.resizeImgroi.width = roi.width * fs_resize_x; + tem.resizeImgroi.y = roi.y * fs_resize_y; + tem.resizeImgroi.height = roi.height * fs_resize_y; + + tem.x_pixel = roi.x + roi.width * 0.5; + tem.y_pixel = roi.y + roi.height * 0.5; + + tem.x_mm = tem.x_pixel * m_fImgage_Scale_X; + tem.y_mm = tem.y_pixel * m_fImgage_Scale_Y; + + tem.CutImgroi = roi; + tem.CutImgroi.x -= CutRoi.x; + tem.CutImgroi.y -= CutRoi.y; + + #ifdef AI_Time + GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); + #endif + return tem; +} int ImageResultJudge::UpdateImgageScale() { @@ -151,6 +213,7 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) map> allQxZoning; set qx_name_list; + m_zoningDrawGroups.clear(); for (int qxidx = 0; qxidx < qxNum; qxidx++) { @@ -184,6 +247,9 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) bool bNG_Status = false; bool bYS_Status = false; bool Judge_Status = false; + int matched_group_id = -1; + float matched_dis = 0; + float matched_num = 0; QX_info->result = QX_RESULT_TYPE_NoJduge; @@ -259,18 +325,6 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) float dis = pParam->paramArr[j].dis; float num = pParam->paramArr[j].num; - if(dis > 0 && num > 0 && allQxZoning[qx_name].size() == 0){ - pQxLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "Dis | Num", "qx_name : %s, dis : %f, num : %f, scale_X : %f, scale_Y : %f", - qx_name.c_str(), dis, num, m_fImgage_Scale_X, m_fImgage_Scale_Y); - singleQxZoningInfo singleQxZoningInfoTemp; - singleQxZoningInfoTemp.Init(); - singleQxZoningInfoTemp.judge_dis = dis; - singleQxZoningInfoTemp.judge_num = num; - singleQxZoningInfoTemp.scale_X = m_fImgage_Scale_X; - singleQxZoningInfoTemp.scale_Y = m_fImgage_Scale_Y; - allQxZoning[qx_name].push_back(singleQxZoningInfoTemp); - } - // 黑缺陷 if (QX_whiteBLACK == CONFIG_QX_WHITE) { @@ -304,6 +358,35 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) flen >= Len && fbreadth >= Breadth) { + if(dis > 0 && num > 0 && ict == ANALYSIS_TYPE_TF) + { + matched_group_id = j; + matched_dis = dis; + matched_num = num; + int if_insert = false; + singleQxInfo current_YS_info(qxidx, QX_RESULT_TYPE_YS, pCenter); + // 查找与当前缺陷参数组标识匹配的分区进行插入 + for(int i = 0; i < allQxZoning[qx_name].size(); i++){ + if(allQxZoning[qx_name][i].param_group_id == matched_group_id && + allQxZoning[qx_name][i].Insert(current_YS_info)){ + if_insert = true; + break; + } + } + if(!if_insert && matched_group_id >= 0){ + singleQxZoningInfo newZoningInfoTemp; + newZoningInfoTemp.Init(); + newZoningInfoTemp.param_group_id = matched_group_id; + newZoningInfoTemp.judge_dis = matched_dis; + newZoningInfoTemp.judge_num = matched_num; + newZoningInfoTemp.center = pCenter; + newZoningInfoTemp.scale_X = m_fImgage_Scale_X; + newZoningInfoTemp.scale_Y = m_fImgage_Scale_Y; + newZoningInfoTemp.qxList.push_back(current_YS_info); + allQxZoning[qx_name].push_back(newZoningInfoTemp); // 插入失败,添加一个新分组 + } + continue; + } nerrortype = 1; result = false; if (ict == ANALYSIS_TYPE_YS) @@ -394,25 +477,6 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) else if (bYS_Status) { QX_info->result = QX_RESULT_TYPE_YS; - int if_insert = false; - singleQxInfo current_YS_info(qxidx, QX_RESULT_TYPE_YS, pCenter); - for(int i = 0; i < allQxZoning[qx_name].size(); i++){ - if(allQxZoning[qx_name][i].Insert(current_YS_info)){ - if_insert = true; - break; - } - } - if(!if_insert && allQxZoning[qx_name].size() > 0){ - singleQxZoningInfo newZoningInfoTemp; - newZoningInfoTemp.Init(); - newZoningInfoTemp.judge_dis = allQxZoning[qx_name][0].judge_dis; - newZoningInfoTemp.judge_num = allQxZoning[qx_name][0].judge_num; - newZoningInfoTemp.center = pCenter; - newZoningInfoTemp.scale_X = m_fImgage_Scale_X; - newZoningInfoTemp.scale_Y = m_fImgage_Scale_Y; - newZoningInfoTemp.qxList.push_back(current_YS_info); - allQxZoning[qx_name].push_back(newZoningInfoTemp); // 插入失败,添加一个新分组 - } } else { @@ -495,53 +559,8 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) // 生成缺陷小图 if (bYS_Status || bNG_Status) { - - // 生成图片 - QXImageResult tem; - tem.idx = qxidx; - cv::Rect CutRoi = GetCutRoi(roi, pImageResult->detImg); - // CheckUtil::printROI(CutRoi, "CutRoi"); - cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT); - - if (pImageResult->detImg.channels() == 1) - { - cv::cvtColor(pImageResult->detImg(CutRoi), tem.srcImg, cv::COLOR_GRAY2BGR); - } - else - { - tem.srcImg = pImageResult->detImg(CutRoi).clone(); - } - cv::resize(tem.srcImg, tem.resizeImg, sz); - + QXImageResult tem = BuildDefectImage(pImageResult, QX_info, qxidx, pCenter, fs_resize_x, fs_resize_y); int nqx_type = ConfigTypeToResultType(config_qx_type); - tem.type = nqx_type; - tem.area = JudgArea; - tem.energy = energy; - tem.hj = grayDis; - tem.max_v = maxValue; - tem.strTypeName = QX_Result_Names[nqx_type]; - tem.qx_Code = QX_Result_Code[nqx_type]; - tem.srcImgroi = roi; - tem.len = flen; - tem.breadth = fbreadth; - tem.qx_type = 0; - tem.fScore = 0; - tem.density = 0; - - tem.resizeImgroi.x = roi.x * fs_resize_x; - tem.resizeImgroi.width = roi.width * fs_resize_x; - tem.resizeImgroi.y = roi.y * fs_resize_y; - tem.resizeImgroi.height = roi.height * fs_resize_y; - - tem.x_pixel = roi.x + roi.width * 0.5; - tem.y_pixel = roi.y + roi.height * 0.5; - - tem.x_mm = tem.x_pixel * m_fImgage_Scale_X; - tem.y_mm = tem.y_pixel * m_fImgage_Scale_Y; - - tem.CutImgroi = roi; - tem.CutImgroi.x -= CutRoi.x; - tem.CutImgroi.y -= CutRoi.y; if (bNG_Status) { @@ -555,22 +574,16 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) m_CheckResult_shareP->nresult = nqx_type; } - GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); m_CheckResult_shareP->qxImageResult.push_back(tem); - pQxLog->bPrintStr = true; pQxLog->AddCheckstr(PrintLevel_4, DET_LOG_LEVEL_3, " result ", " ---name: %s ---code: %s ---srcImgroi.x: %d ---srcImgroi.y: %d\n", tem.strTypeName.c_str(), tem.qx_Code.c_str(), tem.srcImgroi.x, tem.srcImgroi.y); - pQxLog->bPrintStr = false; } else { - if (m_CheckResult_shareP->nYS_result <= ERROR_TYPE_OK) { m_CheckResult_shareP->nYS_result = nqx_type; } - GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); - m_CheckResult_shareP->YS_ImageResult.push_back(tem); } } @@ -581,84 +594,24 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) string qx_name = name; for(int i = 0; i < allQxZoning[qx_name].size(); i++) { - // 个数超过设定值,YS改为NG - if(allQxZoning[qx_name][i].judgeNum()){ + // 个数超过设定值,判NG + if(allQxZoning[qx_name][i].judgeNum()) + { + pImageLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "Zoning NG", " name: %s, group_id: %d, count: %d >= %d → NG", + qx_name.c_str(), allQxZoning[qx_name][i].param_group_id, + (int)allQxZoning[qx_name][i].qxList.size(), allQxZoning[qx_name][i].judge_num); for(int j = 0; j < allQxZoning[qx_name][i].qxList.size(); j++) { int qxidx = allQxZoning[qx_name][i].qxList[j].qxidx; cv::Point pCenter = allQxZoning[qx_name][i].qxList[j].pcenter; QX_ERROR_INFO_ *QX_info = &pDetResult->pQx_ErrorList->at(qxidx); - std::shared_ptr pQxLog = pDetResult->pQx_ErrorList->at(qxidx).detlog; - - pQxLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "YS to NG", " name: %s, idx: %d | Index %d / %d > %d", qx_name.c_str(), qxidx, j + 1, allQxZoning[qx_name][i].qxList.size(), allQxZoning[qx_name][i].judge_num); - - // pQxLog->bPrintStr = true; - cv::Rect roi = QX_info->roi; - float JudgArea = QX_info->JudgArea; - float JudgArea_second = QX_info->JudgArea_second; - float flen = QX_info->flen; - float fbreadth = QX_info->fbreadth; - float grayDis = QX_info->grayDis; - int energy = QX_info->energy; - float fupS = QX_info->fUpIou; - int maxValue = QX_info->maxValue; - float density = QX_info->density; - int QX_whiteBLACK = QX_info->whiteOrBlack; - int config_qx_type = QX_info->nconfig_qx_type; - std::string qx_name = CONFIG_QX_NAME_Names[config_qx_type]; - std::string qx_wb = WHITE_BLCAK_Names[QX_whiteBLACK]; - + QX_info->result = QX_RESULT_TYPE_NG; QX_info->result_name = QX_RESULT_TYPE_Names[QX_info->result]; - std::string resultType = QX_info->result_name; m_CheckResult_shareP->nresult = 1; - // 生成图片 - QXImageResult tem; - tem.idx = qxidx; - cv::Rect CutRoi = GetCutRoi(roi, pImageResult->detImg); - // CheckUtil::printROI(CutRoi, "CutRoi"); - cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT); - - if (pImageResult->detImg.channels() == 1) - { - cv::cvtColor(pImageResult->detImg(CutRoi), tem.srcImg, cv::COLOR_GRAY2BGR); - } - else - { - tem.srcImg = pImageResult->detImg(CutRoi).clone(); - } - cv::resize(tem.srcImg, tem.resizeImg, sz); - - int nqx_type = ConfigTypeToResultType(config_qx_type); - tem.type = nqx_type; - tem.area = JudgArea; - tem.energy = energy; - tem.hj = grayDis; - tem.max_v = maxValue; - tem.strTypeName = QX_Result_Names[nqx_type]; - tem.qx_Code = QX_Result_Code[nqx_type]; - tem.srcImgroi = roi; - tem.len = flen; - tem.breadth = fbreadth; - tem.qx_type = 0; - tem.fScore = 0; - tem.density = 0; - - tem.resizeImgroi.x = roi.x * fs_resize_x; - tem.resizeImgroi.width = roi.width * fs_resize_x; - tem.resizeImgroi.y = roi.y * fs_resize_y; - tem.resizeImgroi.height = roi.height * fs_resize_y; - - tem.x_pixel = roi.x + roi.width * 0.5; - tem.y_pixel = roi.y + roi.height * 0.5; - - tem.x_mm = tem.x_pixel * m_fImgage_Scale_X; - tem.y_mm = tem.y_pixel * m_fImgage_Scale_Y; - - tem.CutImgroi = roi; - tem.CutImgroi.x -= CutRoi.x; - tem.CutImgroi.y -= CutRoi.y; + QXImageResult tem = BuildDefectImage(pImageResult, QX_info, qxidx, pCenter, fs_resize_x, fs_resize_y); + int nqx_type = ConfigTypeToResultType(QX_info->nconfig_qx_type); m_CheckResult_shareP->defectResultList[nqx_type].nresult = 1; m_CheckResult_shareP->defectResultList[nqx_type].keyName = QX_Result_Names[nqx_type]; @@ -669,10 +622,46 @@ int ImageResultJudge::ResultJudge(std::shared_ptr pImageResult) m_CheckResult_shareP->nresult = nqx_type; } - GetAIDetImg(pImageResult, pCenter, tem.AI_in_Img, tem.AI_out_img); m_CheckResult_shareP->qxImageResult.push_back(tem); } - } + } + // 数量未达标,判为YS + else + { + pImageLog->AddCheckstr(PrintLevel_3, DET_LOG_LEVEL_3, "Zoning YS", " name: %s, group_id: %d, count: %d < %d → YS", + qx_name.c_str(), allQxZoning[qx_name][i].param_group_id, + (int)allQxZoning[qx_name][i].qxList.size(), allQxZoning[qx_name][i].judge_num); + for(int j = 0; j < allQxZoning[qx_name][i].qxList.size(); j++) + { + int qxidx = allQxZoning[qx_name][i].qxList[j].qxidx; + cv::Point pCenter = allQxZoning[qx_name][i].qxList[j].pcenter; + QX_ERROR_INFO_ *QX_info = &pDetResult->pQx_ErrorList->at(qxidx); + QX_info->result_name = QX_RESULT_TYPE_Names[QX_info->result]; + m_CheckResult_shareP->nYS_result = 1; + + QXImageResult tem = BuildDefectImage(pImageResult, QX_info, qxidx, pCenter, fs_resize_x, fs_resize_y); + int nqx_type = ConfigTypeToResultType(QX_info->nconfig_qx_type); + + if (m_CheckResult_shareP->nYS_result <= ERROR_TYPE_OK) + { + m_CheckResult_shareP->nYS_result = nqx_type; + } + + m_CheckResult_shareP->YS_ImageResult.push_back(tem); + } + } + + // 收集本组所有缺陷中心点,用于后续连线绘制(至少2个点才有意义) + if(allQxZoning[qx_name][i].qxList.size() >= 2) + { + QxZoningDrawGroup group; + group.isNG = allQxZoning[qx_name][i].judgeNum(); + for(int j = 0; j < allQxZoning[qx_name][i].qxList.size(); j++) + { + group.centers.push_back(allQxZoning[qx_name][i].qxList[j].pcenter); + } + m_zoningDrawGroups.push_back(group); + } } } @@ -971,6 +960,28 @@ int ImageResultJudge::DrawResult(std::shared_ptr pImageResult) } long t2 = CheckUtil::getcurTime(); + // 绘制同组缺陷之间的连线(至少2个点才有连线意义) + cv::Scalar color_line_NG = cv::Scalar(0, 0, 255); // 红色,NG组连线 + cv::Scalar color_line_YS = cv::Scalar(0, 200, 200); // 青黄色,YS组连线 + for (const auto& group : m_zoningDrawGroups) + { + if (group.centers.size() < 2) continue; + + std::shared_ptr draw_line = std::make_shared(); + draw_line->drawImg = m_CheckResult_shareP->resultimg; + draw_line->type = Draw_lines; + draw_line->color = group.isNG ? color_line_NG : color_line_YS; + draw_line->dr = 1; // 线宽 + // 将detection坐标系的中心点缩放到result图像坐标系 + for (const auto& pt : group.centers) + { + draw_line->linePts.push_back(cv::Point( + (int)(pt.x * fs_resize_x), + (int)(pt.y * fs_resize_y))); + } + sendTask(draw_line); + } + // 等待绘制完成 WaiteDrawComplate(); long t3 = CheckUtil::getcurTime(); @@ -1214,6 +1225,19 @@ int ImageResultJudge::DrawResultTask(std::shared_ptr // cv::circle(task->drawImg, task->cp, 3, task->color); } + if (task->type == Draw_lines) + { + if (task->linePts.size() >= 2) + { + int thickness = task->dr > 0 ? task->dr : 1; + for (size_t i = 0; i < task->linePts.size() - 1; i++) + { + cv::line(task->drawImg, task->linePts[i], task->linePts[i + 1], task->color, thickness); + } + // 闭合连线:首尾相连 + cv::line(task->drawImg, task->linePts.back(), task->linePts.front(), task->color, thickness); + } + } return 0; }