|
|
|
|
@ -12,6 +12,19 @@
|
|
|
|
|
#include <omp.h>
|
|
|
|
|
#include "AICommonDefine.h"
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
// 把手判定参数:
|
|
|
|
|
// 1、有参数(参数里绘制了把手区域)时,凸起"沿边长度"与手绘区域对应边长度的比值范围
|
|
|
|
|
#define HANDLE_LEN_MIN_RATIO 0.75f
|
|
|
|
|
#define HANDLE_LEN_MAX_RATIO 1.5f
|
|
|
|
|
// 2、凸起"垂直深度"与手绘区域对应边宽度的比值范围(凸起被图像边界截断时不校深度)
|
|
|
|
|
#define HANDLE_DEPTH_MIN_RATIO 0.8f
|
|
|
|
|
#define HANDLE_DEPTH_MAX_RATIO 1.5f
|
|
|
|
|
// 3、无参数时,按掩膜短边推算沿边长度/垂直深度范围(最小/最大占比)
|
|
|
|
|
#define HANDLE_DEFAULT_MIN_RATIO 0.03f
|
|
|
|
|
#define HANDLE_DEFAULT_MAX_RATIO 0.40f
|
|
|
|
|
// 4、凸起填充率下限、面积/灰阶等判定参数的默认值都在 Handle_Check_Param(CheckConfigDefine.h)里
|
|
|
|
|
|
|
|
|
|
// 用于排序轮廓的比较函数
|
|
|
|
|
static bool compareContourAreas(const vector<Point> &contour1, const vector<Point> &contour2)
|
|
|
|
|
{
|
|
|
|
|
@ -598,6 +611,60 @@ int ImgCheckAnalysisy::CheckRun()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 把手缺失检测:bigmask 的凸起本身不算 NG,只有"参数里画了把手区域但没检出把手"才算 NG */
|
|
|
|
|
{
|
|
|
|
|
if (m_bHandleDetSucc && m_bHandleExpect && !m_bHandleFound)
|
|
|
|
|
{
|
|
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
|
|
|
|
|
|
|
// 上报参数里绘制的把手区域位置:原图坐标 -> 检测图(detImg)坐标
|
|
|
|
|
cv::Rect roi = m_HandleBoxParamImg;
|
|
|
|
|
roi.x -= m_outer_roi.x;
|
|
|
|
|
roi.y -= m_outer_roi.y;
|
|
|
|
|
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
|
|
if (roi.width <= 0 || roi.height <= 0)
|
|
|
|
|
{
|
|
|
|
|
roi = cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
|
|
|
|
|
}
|
|
|
|
|
temerror.roi = roi;
|
|
|
|
|
temerror.area = roi.width * roi.height;
|
|
|
|
|
temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y;
|
|
|
|
|
temerror.JudgArea_second = temerror.JudgArea;
|
|
|
|
|
float w = roi.width;
|
|
|
|
|
float h = roi.height;
|
|
|
|
|
temerror.flen = (w > h ? w : h) * m_fImgage_Scale_X;
|
|
|
|
|
temerror.fbreadth = (w > h ? h : w) * m_fImgage_Scale_Y;
|
|
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_handle_loss;
|
|
|
|
|
temerror.qx_name = CONFIG_QX_NAME_Names[CONFIG_QX_NAME_handle_loss];
|
|
|
|
|
temerror.result = QX_RESULT_TYPE_NG;
|
|
|
|
|
temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG];
|
|
|
|
|
|
|
|
|
|
// 计算把手中心所在的检测区域
|
|
|
|
|
cv::Point pCenter;
|
|
|
|
|
pCenter.x = roi.x + roi.width * 0.5;
|
|
|
|
|
pCenter.y = roi.y + roi.height * 0.5;
|
|
|
|
|
int nmaxregionIdx = 0;
|
|
|
|
|
for (int iregion = 0; iregion < m_DetRoiList.roiList_Src.size(); iregion++)
|
|
|
|
|
{
|
|
|
|
|
const std::vector<cv::Point> &polygon = m_DetRoiList.roiList_Src[iregion];
|
|
|
|
|
double result = cv::pointPolygonTest(polygon, pCenter, false);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
nmaxregionIdx = iregion;
|
|
|
|
|
}
|
|
|
|
|
temerror.detRegionidxList.push_back(nmaxregionIdx);
|
|
|
|
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "把手检测", "%s %d roi [%d %d %d %d] handle param box [%d %d %d %d]",
|
|
|
|
|
temerror.qx_name.c_str(), temerror.Idx, roi.x, roi.y, roi.width, roi.height,
|
|
|
|
|
m_HandleBoxParamImg.x, m_HandleBoxParamImg.y, m_HandleBoxParamImg.width, m_HandleBoxParamImg.height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tag检测 */
|
|
|
|
|
{
|
|
|
|
|
for (const auto &tagRoi : m_tag_roiList)
|
|
|
|
|
@ -2065,95 +2132,262 @@ int ImgCheckAnalysisy::AI_Edge(const cv::Mat &img, cv::RotatedRect &outerRoi, cv
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*使用m_pEdge_Align_Result->bigmask检测把手是否缺失*/
|
|
|
|
|
// 截取把手大致区域(使用把手模板完整 boundingRect,而非整图高度)
|
|
|
|
|
RotatedRect tpl_handle_rroi = m_pFuntion->function.f_supportDet.handleRect;
|
|
|
|
|
Rect handle_rect = tpl_handle_rroi.boundingRect() & Rect(0, 0, m_pEdge_Align_Result->bigmask.cols, m_pEdge_Align_Result->bigmask.rows);
|
|
|
|
|
if (handle_rect.width <= 0 || handle_rect.height <= 0)
|
|
|
|
|
{
|
|
|
|
|
long handle_s = CheckUtil::getcurTime();
|
|
|
|
|
CheckHandleByBigMask(img, m_pEdge_Align_Result->bigmask);
|
|
|
|
|
long handle_e = CheckUtil::getcurTime();
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "handle detect use time %ld", handle_e - handle_s);
|
|
|
|
|
}
|
|
|
|
|
return re;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用 bigmask 检测把手:
|
|
|
|
|
// 1、bigmask 主体近似矩形,把手是主体四周凸起的"类矩形块"
|
|
|
|
|
// 2、把主体四周的凸起抠掉(凸起在该边的边界值上表现为一次突变进入 + 一次反向突变离开),剩下的就是矩形主体
|
|
|
|
|
// 3、按参数里绘制的把手区域尺寸(沿边长度 + 垂直深度,带容差)逐个判定凸起是否为把手
|
|
|
|
|
int ImgCheckAnalysisy::CheckHandleByBigMask(const cv::Mat &img, const cv::Mat &bigmask)
|
|
|
|
|
{
|
|
|
|
|
m_bHandleDetSucc = false;
|
|
|
|
|
m_bHandleFound = false;
|
|
|
|
|
m_bHandleExpect = false;
|
|
|
|
|
m_HandleBoxParamImg = cv::Rect(0, 0, 0, 0);
|
|
|
|
|
m_HandleCandList.clear();
|
|
|
|
|
m_HandleRoiList.clear();
|
|
|
|
|
|
|
|
|
|
if (bigmask.empty())
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "bigmask is empty, skip handle detect");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1、抠出 bigmask 四周凸起的矩形块
|
|
|
|
|
std::vector<Mask_Protrusion_> protrusionList;
|
|
|
|
|
int re = CheckUtil::GetMaskProtrusions(bigmask, protrusionList);
|
|
|
|
|
if (re != 0)
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "GetMaskProtrusions error %d", re);
|
|
|
|
|
return re;
|
|
|
|
|
}
|
|
|
|
|
Mat handle_roi = m_pEdge_Align_Result->bigmask(handle_rect).clone();
|
|
|
|
|
|
|
|
|
|
// 对handle_roi做一下开运算
|
|
|
|
|
Mat element = getStructuringElement(MORPH_RECT, Size(std::max(1, tpl_handle_rroi.boundingRect().width / 3), std::max(1, tpl_handle_rroi.boundingRect().height / 3)));
|
|
|
|
|
morphologyEx(handle_roi, handle_roi, MORPH_OPEN, element);
|
|
|
|
|
m_bHandleDetSucc = true;
|
|
|
|
|
|
|
|
|
|
// imwrite("handle_roi.png", handle_roi);
|
|
|
|
|
// 2、把手参照尺寸 + 判定参数:都来自参数里绘制的把手区域(保留图像 x/y 方向)
|
|
|
|
|
Handle_Check_Param handleParam;
|
|
|
|
|
if (m_pFuntion != NULL)
|
|
|
|
|
{
|
|
|
|
|
const Function_Support_Det &supportDet = m_pFuntion->function.f_supportDet;
|
|
|
|
|
handleParam = supportDet.handleParam;
|
|
|
|
|
if (supportDet.handleRegion.size() > 0)
|
|
|
|
|
{
|
|
|
|
|
m_HandleBoxParamImg = cv::boundingRect(supportDet.handleRegion);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_bHandleExpect = (handleParam.bOpen && m_HandleBoxParamImg.width > 1 && m_HandleBoxParamImg.height > 1);
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle",
|
|
|
|
|
"protrusion num %ld handle param box [%d %d %d %d] expect %s",
|
|
|
|
|
protrusionList.size(), m_HandleBoxParamImg.x, m_HandleBoxParamImg.y,
|
|
|
|
|
m_HandleBoxParamImg.width, m_HandleBoxParamImg.height, BOOL_TO_STR(m_bHandleExpect));
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle", "%s", handleParam.GetInfo("judge").c_str());
|
|
|
|
|
|
|
|
|
|
// handle_roi找到最大连通域,和tpl_handle_rroi做面积形状比较,差异过大判为把手缺失,添加到缺陷并NG
|
|
|
|
|
// 灰阶判定是预留项:只有开启时才做灰度换算,避免无谓开销
|
|
|
|
|
cv::Mat grayImg;
|
|
|
|
|
if (handleParam.bJudgeGray && !img.empty())
|
|
|
|
|
{
|
|
|
|
|
Function_Support_Det &handleDet = m_pFuntion->function.f_supportDet;
|
|
|
|
|
if (handleDet.bOpen && tpl_handle_rroi.size.width > 0 && tpl_handle_rroi.size.height > 0 && !handle_roi.empty())
|
|
|
|
|
if (img.channels() != 1)
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::vector<cv::Point>> handle_contours;
|
|
|
|
|
cv::findContours(handle_roi, handle_contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
|
|
|
|
cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
grayImg = img;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bHandleLoss = false;
|
|
|
|
|
if (handle_contours.empty())
|
|
|
|
|
{
|
|
|
|
|
bHandleLoss = true; // 没有连通域,把手完全缺失
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 取最大连通域
|
|
|
|
|
std::sort(handle_contours.begin(), handle_contours.end(), compareContourAreas);
|
|
|
|
|
cv::RotatedRect cur_handle_rrect = cv::minAreaRect(handle_contours[0]);
|
|
|
|
|
double curArea = cv::contourArea(handle_contours[0]);
|
|
|
|
|
|
|
|
|
|
// 面积比较
|
|
|
|
|
double tplArea = tpl_handle_rroi.size.width * tpl_handle_rroi.size.height;
|
|
|
|
|
double areaRatio = (tplArea > 0.0) ? (curArea / tplArea) : 0.0;
|
|
|
|
|
|
|
|
|
|
// 形状比较(长宽比)
|
|
|
|
|
float tplW = std::max(tpl_handle_rroi.size.width, tpl_handle_rroi.size.height);
|
|
|
|
|
float tplH = std::min(tpl_handle_rroi.size.width, tpl_handle_rroi.size.height);
|
|
|
|
|
float curW = std::max(cur_handle_rrect.size.width, cur_handle_rrect.size.height);
|
|
|
|
|
float curH = std::min(cur_handle_rrect.size.width, cur_handle_rrect.size.height);
|
|
|
|
|
float tplRatio = (tplH > 0.0f) ? (tplW / tplH) : 0.0f;
|
|
|
|
|
float curRatio = (curH > 0.0f) ? (curW / curH) : 0.0f;
|
|
|
|
|
float ratioDiff = (tplRatio > 0.0f) ? fabs(curRatio - tplRatio) / tplRatio : 0.0f;
|
|
|
|
|
|
|
|
|
|
// 面积差异过大 或 形状差异过大 判为把手缺失
|
|
|
|
|
if (areaRatio < 0.6f || ratioDiff > 0.6f)
|
|
|
|
|
{
|
|
|
|
|
bHandleLoss = true;
|
|
|
|
|
}
|
|
|
|
|
// 3、逐个凸起:先算实测特征(沿边长度/深度/面积/灰度差),再做判定
|
|
|
|
|
cv::Size maskSize(bigmask.cols, bigmask.rows);
|
|
|
|
|
for (size_t i = 0; i < protrusionList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
Handle_Candidate_ cand;
|
|
|
|
|
cand.protr = protrusionList[i];
|
|
|
|
|
// 左/右凸起:沿边为 y 方向,深度为 x 方向;上/下凸起相反
|
|
|
|
|
bool bHorz = (cand.protr.nSide == 0 || cand.protr.nSide == 1);
|
|
|
|
|
cand.nLen = bHorz ? cand.protr.roi.height : cand.protr.roi.width;
|
|
|
|
|
cand.nDepth = bHorz ? cand.protr.roi.width : cand.protr.roi.height;
|
|
|
|
|
if (!grayImg.empty())
|
|
|
|
|
{
|
|
|
|
|
cand.fGrayDiff = CalProtrusionGrayDiff(grayImg, cand.protr.roi);
|
|
|
|
|
}
|
|
|
|
|
cand.bHandle = IsHandleProtrusion(cand, handleParam, m_HandleBoxParamImg, maskSize);
|
|
|
|
|
if (cand.bHandle)
|
|
|
|
|
{
|
|
|
|
|
m_HandleRoiList.push_back(cand.protr.roi);
|
|
|
|
|
}
|
|
|
|
|
m_HandleCandList.push_back(cand);
|
|
|
|
|
// grayDiff 打印 -1 表示本次未开启灰阶判定(未计算)
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "Handle",
|
|
|
|
|
"protrusion %ld side %d roi [%d %d %d %d] len %d depth %d area %0.0f fill %0.2f grayDiff %0.1f -> %s",
|
|
|
|
|
i, cand.protr.nSide, cand.protr.roi.x, cand.protr.roi.y, cand.protr.roi.width, cand.protr.roi.height,
|
|
|
|
|
cand.nLen, cand.nDepth, cand.protr.fArea, cand.protr.fFillRatio,
|
|
|
|
|
grayImg.empty() ? -1.0f : cand.fGrayDiff, cand.bHandle ? "handle" : "not handle");
|
|
|
|
|
}
|
|
|
|
|
m_bHandleFound = (!m_HandleRoiList.empty());
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "把手检测", "areaRatio %f ratioDiff %f", areaRatio, ratioDiff);
|
|
|
|
|
}
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, "Handle", "handle num %ld found %s use handle param %s",
|
|
|
|
|
m_HandleRoiList.size(), BOOL_TO_STR(m_bHandleFound), BOOL_TO_STR(m_bHandleExpect));
|
|
|
|
|
|
|
|
|
|
if (bHandleLoss)
|
|
|
|
|
{
|
|
|
|
|
QX_ERROR_INFO_ temerror;
|
|
|
|
|
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
|
|
|
|
|
// 参数里绘制了把手区域,但 bigmask 四周没有满足判定条件的凸起 -> 把手缺失(NG 由 CheckRun 上报)
|
|
|
|
|
if (m_bHandleExpect && !m_bHandleFound)
|
|
|
|
|
{
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, "Handle", "handle is missing !!");
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, DET_LOG_LEVEL_3, "Handle", "handle param box [%d %d %d %d]",
|
|
|
|
|
m_HandleBoxParamImg.x, m_HandleBoxParamImg.y, m_HandleBoxParamImg.width, m_HandleBoxParamImg.height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 把手模板 roi 在原图坐标系,转换为检测图(detImg)坐标系
|
|
|
|
|
cv::Rect roi = tpl_handle_rroi.boundingRect();
|
|
|
|
|
roi.x -= outerRoi.boundingRect().x;
|
|
|
|
|
roi.y -= outerRoi.boundingRect().y;
|
|
|
|
|
roi &= cv::Rect(0, 0, outerRoi.boundingRect().width, outerRoi.boundingRect().height);
|
|
|
|
|
temerror.roi = roi;
|
|
|
|
|
temerror.area = roi.width * roi.height;
|
|
|
|
|
temerror.JudgArea = roi.width * m_fImgage_Scale_X * roi.height * m_fImgage_Scale_Y;
|
|
|
|
|
temerror.JudgArea_second = temerror.JudgArea;
|
|
|
|
|
float w = tpl_handle_rroi.size.width;
|
|
|
|
|
float h = tpl_handle_rroi.size.height;
|
|
|
|
|
temerror.flen = (w > h ? w : h) * m_fImgage_Scale_X;
|
|
|
|
|
temerror.fbreadth = (w > h ? h : w) * m_fImgage_Scale_Y;
|
|
|
|
|
temerror.nconfig_qx_type = CONFIG_QX_NAME_support_loss;
|
|
|
|
|
temerror.qx_name = CONFIG_QX_NAME_Names[temerror.nconfig_qx_type];
|
|
|
|
|
temerror.result = QX_RESULT_TYPE_NG;
|
|
|
|
|
temerror.result_name = QX_RESULT_TYPE_Names[QX_RESULT_TYPE_NG];
|
|
|
|
|
temerror.detRegionidxList.push_back(0);
|
|
|
|
|
// 4、存过程图:凸起红框、把手绿框(仅调试时)
|
|
|
|
|
if (DetImgInfo_shareP->bsaveProcessImg)
|
|
|
|
|
{
|
|
|
|
|
cv::Mat show;
|
|
|
|
|
if (bigmask.channels() == 1)
|
|
|
|
|
{
|
|
|
|
|
cv::cvtColor(bigmask, show, cv::COLOR_GRAY2BGR);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
show = bigmask.clone();
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < m_HandleCandList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
cv::rectangle(show, m_HandleCandList[i].protr.roi, cv::Scalar(0, 0, 255), 8);
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < m_HandleRoiList.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
cv::rectangle(show, m_HandleRoiList[i], cv::Scalar(0, 255, 0), 12);
|
|
|
|
|
}
|
|
|
|
|
cv::Mat showSmall;
|
|
|
|
|
cv::resize(show, showSmall, cv::Size(show.cols / 4, show.rows / 4));
|
|
|
|
|
std::vector<int> paramJpg = {cv::IMWRITE_JPEG_QUALITY, 90};
|
|
|
|
|
cv::imwrite(DetImgInfo_shareP->strChannel + "_handle_det.jpg", showSmall, paramJpg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pDetResult->pQx_ErrorList->push_back(temerror);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pdetlog->AddCheckstr(PrintLevel_0, "把手检测", "handle loss NG roi [%d %d %d %d]", roi.x, roi.y, roi.width, roi.height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 判断凸起是否为把手:参数里每一项"开启"的判定条件都满足才算把手
|
|
|
|
|
// 尺寸参照参数里绘制的把手区域(handleBox,保留图像 x/y 方向):
|
|
|
|
|
// 沿边长度 对应 handleBox 在该边方向上的长度,垂直深度 对应 handleBox 的宽度;
|
|
|
|
|
// 凸起外边缘被图像边界截断时,深度不可信,只校验沿边长度
|
|
|
|
|
// 参数里没写显式像素范围时,按把手区域尺寸的比例推算(HANDLE_LEN_*_RATIO / HANDLE_DEPTH_*_RATIO)
|
|
|
|
|
bool ImgCheckAnalysisy::IsHandleProtrusion(const Handle_Candidate_ &cand, const Handle_Check_Param ¶m,
|
|
|
|
|
const cv::Rect &handleBox, const cv::Size &maskSize)
|
|
|
|
|
{
|
|
|
|
|
const Mask_Protrusion_ &protr = cand.protr;
|
|
|
|
|
if (protr.roi.width <= 0 || protr.roi.height <= 0)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return re;
|
|
|
|
|
// 左/右凸起:沿边为 y 方向(长度=height),深度为 x 方向(width);上/下凸起相反
|
|
|
|
|
const bool bHorz = (protr.nSide == 0 || protr.nSide == 1);
|
|
|
|
|
// 凸起外边缘是否贴到图像边界(被截断)
|
|
|
|
|
bool bClip = bHorz ? (protr.roi.x <= 0 || protr.roi.x + protr.roi.width >= maskSize.width)
|
|
|
|
|
: (protr.roi.y <= 0 || protr.roi.y + protr.roi.height >= maskSize.height);
|
|
|
|
|
|
|
|
|
|
// 该凸起对应的期望尺寸范围
|
|
|
|
|
int nMinLen = 0, nMaxLen = 0, nMinDepth = 0, nMaxDepth = 0;
|
|
|
|
|
if (handleBox.width > 1 && handleBox.height > 1)
|
|
|
|
|
{
|
|
|
|
|
int nExpLen = bHorz ? handleBox.height : handleBox.width;
|
|
|
|
|
int nExpDepth = bHorz ? handleBox.width : handleBox.height;
|
|
|
|
|
nMinLen = (int)(nExpLen * HANDLE_LEN_MIN_RATIO);
|
|
|
|
|
nMaxLen = (int)(nExpLen * HANDLE_LEN_MAX_RATIO);
|
|
|
|
|
nMinDepth = (int)(nExpDepth * HANDLE_DEPTH_MIN_RATIO);
|
|
|
|
|
nMaxDepth = (int)(nExpDepth * HANDLE_DEPTH_MAX_RATIO);
|
|
|
|
|
}
|
|
|
|
|
else // 参数里没画把手区域:按掩膜短边推算一个大致范围
|
|
|
|
|
{
|
|
|
|
|
int nMinSide = std::min(maskSize.width, maskSize.height);
|
|
|
|
|
nMinLen = nMinDepth = (int)(nMinSide * HANDLE_DEFAULT_MIN_RATIO);
|
|
|
|
|
nMaxLen = nMaxDepth = (int)(nMinSide * HANDLE_DEFAULT_MAX_RATIO);
|
|
|
|
|
}
|
|
|
|
|
// 参数里写了显式像素范围时以参数值为准
|
|
|
|
|
if (param.bJudgeLen && param.fLenMin > 0)
|
|
|
|
|
{
|
|
|
|
|
nMinLen = (int)param.fLenMin;
|
|
|
|
|
}
|
|
|
|
|
if (param.bJudgeLen && param.fLenMax > 0)
|
|
|
|
|
{
|
|
|
|
|
nMaxLen = (int)param.fLenMax;
|
|
|
|
|
}
|
|
|
|
|
if (param.bJudgeDepth && param.fDepthMin > 0)
|
|
|
|
|
{
|
|
|
|
|
nMinDepth = (int)param.fDepthMin;
|
|
|
|
|
}
|
|
|
|
|
if (param.bJudgeDepth && param.fDepthMax > 0)
|
|
|
|
|
{
|
|
|
|
|
nMaxDepth = (int)param.fDepthMax;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 以下条件逐条校验,任意一条不满足即不是把手
|
|
|
|
|
// 1、类似矩形块:凸起区域内掩膜填充率要高
|
|
|
|
|
if (param.bJudgeFill && protr.fFillRatio < param.fFillMin)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 2、沿边长度
|
|
|
|
|
if (param.bJudgeLen && (cand.nLen < nMinLen || cand.nLen > nMaxLen))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 3、垂直深度(凸起被图像边界截断时跳过)
|
|
|
|
|
if (param.bJudgeDepth && !bClip && (cand.nDepth < nMinDepth || cand.nDepth > nMaxDepth))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 4、预留:凸起面积
|
|
|
|
|
if (param.bJudgeArea && param.fAreaMax > param.fAreaMin &&
|
|
|
|
|
(protr.fArea < param.fAreaMin || protr.fArea > param.fAreaMax))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 5、预留:凸起与背景的灰度差
|
|
|
|
|
if (param.bJudgeGray && param.fGrayMax > param.fGrayMin &&
|
|
|
|
|
(cand.fGrayDiff < param.fGrayMin || cand.fGrayDiff > param.fGrayMax))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算凸起区域与周边背景的平均灰度差(灰阶判定的预留量测值,grayImg 需为单通道)
|
|
|
|
|
float ImgCheckAnalysisy::CalProtrusionGrayDiff(const cv::Mat &grayImg, const cv::Rect &roi)
|
|
|
|
|
{
|
|
|
|
|
if (grayImg.empty() || grayImg.channels() != 1 || roi.width <= 0 || roi.height <= 0)
|
|
|
|
|
{
|
|
|
|
|
return 0.0f;
|
|
|
|
|
}
|
|
|
|
|
// 凸起区域通常整个都在掩膜内,向外扩一圈才能取到背景
|
|
|
|
|
int nExpand = std::max(20, std::min(roi.width, roi.height) / 2);
|
|
|
|
|
cv::Rect roiBg(roi.x - nExpand, roi.y - nExpand, roi.width + 2 * nExpand, roi.height + 2 * nExpand);
|
|
|
|
|
roiBg &= cv::Rect(0, 0, grayImg.cols, grayImg.rows);
|
|
|
|
|
if (roiBg.width <= 0 || roiBg.height <= 0)
|
|
|
|
|
{
|
|
|
|
|
return 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv::Mat grayRoi = grayImg(roiBg);
|
|
|
|
|
cv::Mat maskIn = cv::Mat::zeros(roiBg.size(), CV_8UC1);
|
|
|
|
|
cv::Rect inter = roi & roiBg;
|
|
|
|
|
cv::rectangle(maskIn, inter - roiBg.tl(), cv::Scalar(255), cv::FILLED);
|
|
|
|
|
cv::Mat maskOut;
|
|
|
|
|
cv::bitwise_not(maskIn, maskOut);
|
|
|
|
|
if (cv::countNonZero(maskIn) <= 0 || cv::countNonZero(maskOut) <= 0)
|
|
|
|
|
{
|
|
|
|
|
return 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double fMeanIn = cv::mean(grayRoi, maskIn)[0];
|
|
|
|
|
double fMeanOut = cv::mean(grayRoi, maskOut)[0];
|
|
|
|
|
double fDiff = fMeanIn - fMeanOut;
|
|
|
|
|
return (float)(fDiff >= 0 ? fDiff : -fDiff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ImgCheckAnalysisy::CalProductSize()
|
|
|
|
|
|