feat 添加旋转矫正背板

dev_lsy
liusiyang 1 month ago
parent 25319d34a2
commit feb5895b30

@ -223,6 +223,31 @@ struct Detect_ROI_Config
roiList_Src.push_back(adjustedPolygon);
roiList_Show.push_back(adjustedPolygon_size);
}
// 用 2x3 仿射矩阵(参数/原图 -> 检测图)更新多边形;点变换后已在检测图坐标系,只做缩放
void Update_Mat(const std::vector<cv::Point> &list, const cv::Mat &matParamToDet, float fScale_x, float fScale_y)
{
std::vector<cv::Point> adjustedPolygon;
std::vector<cv::Point> adjustedPolygon_size;
for (const auto &point : list)
{
cv::Point2f pt = CheckUtil::transformPoint(cv::Point2f((float)point.x, (float)point.y), matParamToDet);
cv::Point adjustedPoint(cvRound(pt.x), cvRound(pt.y));
if (adjustedPoint.y < 0)
{
adjustedPoint.y = 0;
}
cv::Point sizep;
sizep.x = adjustedPoint.x * fScale_x;
sizep.y = adjustedPoint.y * fScale_y;
adjustedPolygon.push_back(adjustedPoint);
adjustedPolygon_size.push_back(sizep);
}
// 将调整后的多边形添加到roiList_Src
roiList_Src.push_back(adjustedPolygon);
roiList_Show.push_back(adjustedPolygon_size);
}
// 判断点是否在指定idx的ROI内
bool isPointInROI(int idx, const cv::Point &point)
{

@ -92,6 +92,7 @@ struct One_Image_CheckResult_
cv::Rect CutRoi; // 当前检测图片 对应的裁切区域
cv::Rect Param_CropRoi; // 参数模版 对应的裁切区域
std::shared_ptr<std::vector<QX_ERROR_INFO_>> pQx_ErrorList; // 缺陷错误信息
cv::Mat detToSrcMat; // 检测图坐标 -> 原图坐标 的 2x3 仿射矩阵(检测图摆正时有效; 空表示按 CutRoi 平移映射)
void print(std::string str = "")
{
std::cout << str << ": "

@ -276,6 +276,12 @@ private:
cv::Rect m_inner_roi;
cv::Rect m_Crop_Roi_paramImg; // 裁切在参数模板图上的 位置。
// 检测图摆正(去旋转)相关:用 m_outer_rroi 建立 原图 -> 检测图 的仿射映射
cv::Mat m_matSrc2Det; // 原图 -> 检测图(摆正) 2x3 仿射矩阵
cv::Mat m_matDet2Src; // 检测图(摆正) -> 原图 2x3 仿射矩阵
cv::Mat m_matParam2Det; // 参数(模板)坐标 -> 检测图(摆正) 2x3 仿射矩阵
cv::Rect m_deskewPadRoi; // 摆正时原图上的裁剪区域(即 warp 的输入范围)
std::string m_strCurDetChannel; // 当前处理的图片通道
// // 检测控制参数

@ -18,6 +18,18 @@ Json::Value CheckResultJson::toJsonValue()
root["Param_CropRoi"]["width"] = m_pOneImgDetResult->Param_CropRoi.width;
root["Param_CropRoi"]["height"] = m_pOneImgDetResult->Param_CropRoi.height;
}
// 检测图(摆正) -> 原图 的 2x3 仿射矩阵(用于复测/外部分析还原缺陷小图)
if (!m_pOneImgDetResult->detToSrcMat.empty() &&
m_pOneImgDetResult->detToSrcMat.rows == 2 && m_pOneImgDetResult->detToSrcMat.cols == 3)
{
const double *m = m_pOneImgDetResult->detToSrcMat.ptr<double>(0);
root["detToSrc"]["m00"] = m[0];
root["detToSrc"]["m01"] = m[1];
root["detToSrc"]["m02"] = m[2];
root["detToSrc"]["m10"] = m[3];
root["detToSrc"]["m11"] = m[4];
root["detToSrc"]["m12"] = m[5];
}
for (int i = 0; i < m_pOneImgDetResult->pQx_ErrorList->size(); i++)
{
QX_ERROR_INFO_ *p = &m_pOneImgDetResult->pQx_ErrorList->at(i);
@ -84,6 +96,15 @@ void CheckResultJson::toObjectFromValue(Json::Value root)
m_pOneImgDetResult->Param_CropRoi.height = root["Param_CropRoi"]["height"].asInt();
}
}
// 检测图(摆正) -> 原图 的 2x3 仿射矩阵(旧 JSON 没有该字段时按 CutRoi 平移兼容)
{
auto value = root["detToSrc"];
if (value.isObject() && value.isMember("m00"))
{
m_pOneImgDetResult->detToSrcMat = (cv::Mat_<double>(2, 3) << value["m00"].asDouble(), value["m01"].asDouble(), value["m02"].asDouble(),
value["m10"].asDouble(), value["m11"].asDouble(), value["m12"].asDouble());
}
}
{
if (root.isMember("Qx"))
{

@ -1,6 +1,72 @@
#include "ImageResultJudge.h"
// 检测图坐标 -> 原图坐标(检测图摆正时 detToSrcMat 非空; 否则退回 CutRoi 平移)
static cv::Rect DetRoiToSrcRoi(const cv::Mat &detToSrcMat, cv::Rect detRoi, const cv::Rect &cutRoi, const cv::Size &srcSize)
{
if (detToSrcMat.empty() || detToSrcMat.rows != 2 || detToSrcMat.cols != 3)
{
cv::Rect srcRoi = detRoi;
srcRoi.x += cutRoi.x;
srcRoi.y += cutRoi.y;
return srcRoi;
}
const double *m = detToSrcMat.ptr<double>(0);
cv::Point2f pts[4] = {
cv::Point2f((float)detRoi.x, (float)detRoi.y),
cv::Point2f((float)(detRoi.x + detRoi.width), (float)detRoi.y),
cv::Point2f((float)(detRoi.x + detRoi.width), (float)(detRoi.y + detRoi.height)),
cv::Point2f((float)detRoi.x, (float)(detRoi.y + detRoi.height))};
float fminX = 1e18f, fmaxX = -1e18f, fminY = 1e18f, fmaxY = -1e18f;
for (int i = 0; i < 4; i++)
{
float x = (float)(m[0] * pts[i].x + m[1] * pts[i].y + m[2]);
float y = (float)(m[3] * pts[i].x + m[4] * pts[i].y + m[5]);
if (x < fminX)
fminX = x;
if (x > fmaxX)
fmaxX = x;
if (y < fminY)
fminY = y;
if (y > fmaxY)
fmaxY = y;
}
cv::Rect srcRoi(cvFloor(fminX), cvFloor(fminY), cvCeil(fmaxX) - cvFloor(fminX), cvCeil(fmaxY) - cvFloor(fminY));
if (srcSize.width > 0 && srcSize.height > 0)
{
srcRoi &= cv::Rect(0, 0, srcSize.width, srcSize.height);
}
return srcRoi;
}
// 从原图取缺陷小图:detCutRoi 为检测图坐标下的截取框;输出保持与检测图一致的摆正方向(原图通道)
static bool WarpSrcCutImg(const cv::Mat &srcImg, const cv::Mat &detToSrcMat, const cv::Rect &detCutRoi, cv::Mat &outPatch)
{
outPatch.release();
if (srcImg.empty() || detToSrcMat.empty() || detToSrcMat.rows != 2 || detToSrcMat.cols != 3 ||
detCutRoi.width <= 0 || detCutRoi.height <= 0)
{
return false;
}
// detCutRoi 四角映射回原图,得到需要取的原图范围
cv::Rect srcBBox = DetRoiToSrcRoi(detToSrcMat, detCutRoi, cv::Rect(0, 0, 0, 0), srcImg.size());
if (srcBBox.width <= 0 || srcBBox.height <= 0)
{
return false;
}
// dst 为截取框局部坐标, src 为原图坐标(再平移到 srcBBox 内):
// src = detToSrcMat * (dst + detCutRoi.tl()) - srcBBox.tl()
cv::Mat mat = detToSrcMat.clone();
mat.at<double>(0, 2) += mat.at<double>(0, 0) * detCutRoi.x + mat.at<double>(0, 1) * detCutRoi.y - srcBBox.x;
mat.at<double>(1, 2) += mat.at<double>(1, 0) * detCutRoi.x + mat.at<double>(1, 1) * detCutRoi.y - srcBBox.y;
cv::warpAffine(srcImg(srcBBox), outPatch, mat, detCutRoi.size(), cv::INTER_LINEAR, cv::BORDER_REPLICATE);
return !outPatch.empty();
}
ImageResultJudge::ImageResultJudge(/* args */)
{
m_pCommonAnalysisyConfig = NULL;
@ -510,19 +576,28 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
// CheckUtil::printROI(CutRoi, "CutRoi");
cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT);
// 缺陷小图直接取自原图裁剪区(保持原图通道 -> 真实彩色),ReJson/异常时退回检测图
// 缺陷小图直接取自原图裁剪区(保持原图通道 -> 真实彩色);检测图摆正时按仿射取回(保持摆正方向),
// ReJson/异常时退回检测图
{
const cv::Mat &srcImg = pImageResult->result->in_shareImage->img;
int nOffsetX = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.x : 0;
int nOffsetY = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.y : 0;
cv::Rect srcCanvasRoi(CutRoi.x + nOffsetX, CutRoi.y + nOffsetY, CutRoi.width, CutRoi.height);
if (!srcImg.empty() && CheckUtil::RoiInImg(srcCanvasRoi, srcImg))
bool bGot = false;
if (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson)
{
tem.srcImg = srcImg(srcCanvasRoi).clone();
bGot = WarpSrcCutImg(srcImg, pDetResult->detToSrcMat, CutRoi, tem.srcImg);
}
else
if (!bGot)
{
tem.srcImg = pImageResult->detImg(CutRoi).clone();
int nOffsetX = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.x : 0;
int nOffsetY = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.y : 0;
cv::Rect srcCanvasRoi(CutRoi.x + nOffsetX, CutRoi.y + nOffsetY, CutRoi.width, CutRoi.height);
if (!srcImg.empty() && CheckUtil::RoiInImg(srcCanvasRoi, srcImg))
{
tem.srcImg = srcImg(srcCanvasRoi).clone();
}
else
{
tem.srcImg = pImageResult->detImg(CutRoi).clone();
}
}
}
cv::resize(tem.srcImg, tem.resizeImg, sz);
@ -535,9 +610,12 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
tem.max_v = maxValue;
tem.strTypeName = QX_Result_Names[nqx_type];
tem.qx_Code = QX_Result_Code[nqx_type];
tem.srcImgroi = roi;
tem.srcImgroi.x += pDetResult->CutRoi.x;
tem.srcImgroi.y += pDetResult->CutRoi.y;
// 缺陷 roi 映射回原图(摆正时按仿射, 否则按 CutRoi 平移)
{
const cv::Mat &srcSizeImg = pImageResult->result->in_shareImage->img;
tem.srcImgroi = DetRoiToSrcRoi(pDetResult->detToSrcMat, roi, pDetResult->CutRoi,
srcSizeImg.empty() ? pImageResult->detImg.size() : srcSizeImg.size());
}
tem.len = flen;
tem.breadth = fbreadth;
tem.qx_type = 0;
@ -634,19 +712,28 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
// CheckUtil::printROI(CutRoi, "CutRoi");
cv::Size sz = cv::Size(QX_SAMLLIMG_WIDTH, QX_SAMLLIMG_HEIGHT);
// 缺陷小图直接取自原图裁剪区(保持原图通道 -> 真实彩色),ReJson/异常时退回检测图
// 缺陷小图直接取自原图裁剪区(保持原图通道 -> 真实彩色);检测图摆正时按仿射取回(保持摆正方向),
// ReJson/异常时退回检测图
{
const cv::Mat &srcImg = pImageResult->result->in_shareImage->img;
int nOffsetX = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.x : 0;
int nOffsetY = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.y : 0;
cv::Rect srcCanvasRoi(CutRoi.x + nOffsetX, CutRoi.y + nOffsetY, CutRoi.width, CutRoi.height);
if (!srcImg.empty() && CheckUtil::RoiInImg(srcCanvasRoi, srcImg))
bool bGot = false;
if (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson)
{
tem.srcImg = srcImg(srcCanvasRoi).clone();
bGot = WarpSrcCutImg(srcImg, pDetResult->detToSrcMat, CutRoi, tem.srcImg);
}
else
if (!bGot)
{
tem.srcImg = pImageResult->detImg(CutRoi).clone();
int nOffsetX = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.x : 0;
int nOffsetY = (pImageResult->result->in_shareImage->Det_Mode != DET_MODE_ReJson) ? pDetResult->CutRoi.y : 0;
cv::Rect srcCanvasRoi(CutRoi.x + nOffsetX, CutRoi.y + nOffsetY, CutRoi.width, CutRoi.height);
if (!srcImg.empty() && CheckUtil::RoiInImg(srcCanvasRoi, srcImg))
{
tem.srcImg = srcImg(srcCanvasRoi).clone();
}
else
{
tem.srcImg = pImageResult->detImg(CutRoi).clone();
}
}
}
cv::resize(tem.srcImg, tem.resizeImg, sz);
@ -659,9 +746,12 @@ int ImageResultJudge::ResultJudge(std::shared_ptr<ImageAllResult> pImageResult)
tem.max_v = maxValue;
tem.strTypeName = QX_Result_Names[nqx_type];
tem.qx_Code = QX_Result_Code[nqx_type];
tem.srcImgroi = roi;
tem.srcImgroi.x += pDetResult->CutRoi.x;
tem.srcImgroi.y += pDetResult->CutRoi.y;
// 缺陷 roi 映射回原图(摆正时按仿射, 否则按 CutRoi 平移)
{
const cv::Mat &srcSizeImg = pImageResult->result->in_shareImage->img;
tem.srcImgroi = DetRoiToSrcRoi(pDetResult->detToSrcMat, roi, pDetResult->CutRoi,
srcSizeImg.empty() ? pImageResult->detImg.size() : srcSizeImg.size());
}
tem.len = flen;
tem.breadth = fbreadth;
tem.qx_type = 0;

@ -28,6 +28,66 @@ static bool compareContourAreas(const vector<Point> &contour1, const vector<Poin
double j = contourArea(contour2);
return (i > j);
}
// 2x3 仿射矩阵小工具:变换点、矩形外接、组合矩阵(检测图摆正用)
static cv::Point2f MatAffinePoint(const cv::Mat &mat32, const cv::Point2f &pt)
{
if (mat32.empty() || mat32.rows != 2 || mat32.cols != 3)
{
return pt;
}
const double *m = mat32.ptr<double>(0);
return cv::Point2f((float)(m[0] * pt.x + m[1] * pt.y + m[2]),
(float)(m[3] * pt.x + m[4] * pt.y + m[5]));
}
// 矩形四角经仿射变换后取外接矩形
static cv::Rect MatAffineRect(const cv::Mat &mat32, const cv::Rect &roi)
{
std::vector<cv::Point2f> pts(4);
pts[0] = MatAffinePoint(mat32, cv::Point2f((float)roi.x, (float)roi.y));
pts[1] = MatAffinePoint(mat32, cv::Point2f((float)(roi.x + roi.width), (float)roi.y));
pts[2] = MatAffinePoint(mat32, cv::Point2f((float)(roi.x + roi.width), (float)(roi.y + roi.height)));
pts[3] = MatAffinePoint(mat32, cv::Point2f((float)roi.x, (float)(roi.y + roi.height)));
float fminX = pts[0].x, fmaxX = pts[0].x, fminY = pts[0].y, fmaxY = pts[0].y;
for (int i = 1; i < 4; i++)
{
if (pts[i].x < fminX)
fminX = pts[i].x;
if (pts[i].x > fmaxX)
fmaxX = pts[i].x;
if (pts[i].y < fminY)
fminY = pts[i].y;
if (pts[i].y > fmaxY)
fmaxY = pts[i].y;
}
int x0 = cvFloor(fminX);
int y0 = cvFloor(fminY);
int x1 = cvCeil(fmaxX);
int y1 = cvCeil(fmaxY);
return cv::Rect(x0, y0, x1 - x0, y1 - y0);
}
// 组合仿射:先应用 mB 再应用 mA,即 mA ∘ mB,结果仍为 2x3
static cv::Mat MatAffineCompose(const cv::Mat &mA, const cv::Mat &mB)
{
if (mA.empty() || mA.rows != 2 || mA.cols != 3)
{
return mB.clone();
}
if (mB.empty() || mB.rows != 2 || mB.cols != 3)
{
return mA.clone();
}
cv::Mat hA = cv::Mat::eye(3, 3, CV_64F);
cv::Mat hB = cv::Mat::eye(3, 3, CV_64F);
mA.copyTo(hA(cv::Rect(0, 0, 3, 2)));
mB.copyTo(hB(cv::Rect(0, 0, 3, 2)));
cv::Mat hC = hA * hB;
return hC(cv::Rect(0, 0, 3, 2)).clone();
}
ImgCheckAnalysisy::ImgCheckAnalysisy()
: m_pAI_Edge_Algin(m_pdetlog)
{
@ -492,32 +552,86 @@ int ImgCheckAnalysisy::CheckRun()
long time_edge_e = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_0, "2、pre detect", "-------------------------AI_Edge------------succ---time %ld----\n", time_edge_e - time_edge_s);
/*投影对齐模板*/
// Adapt_Config(m_tplOuterRect, m_outer_rroi, m_CheckResult_shareP->in_shareImage->img);
m_outer_roi = m_outer_rroi.boundingRect();
// 旋转矩形 boundingRect 在图像边缘可能越界(如 x=-1),裁剪到图像范围内,避免 image(roi) 越界崩溃
m_outer_roi &= cv::Rect(0, 0, m_CheckResult_shareP->in_shareImage->img.cols, m_CheckResult_shareP->in_shareImage->img.rows);
m_Crop_Roi_paramImg = m_outer_roi;
m_pImageAllResult->pDetResult->CutRoi = m_outer_roi;
m_pImageAllResult->pDetResult->Param_CropRoi = m_Crop_Roi_paramImg;
long time_calan_e = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "-------------------------Adapt_Config------------succ---time %ld----\n", time_calan_e - time_edge_s);
/*生成检测图:用 m_outer_rroi(旋转矩形)确定产品区域,并把该区域旋转摆正后输出;
原图坐标 -> 检测图坐标 的映射存入 m_matSrc2Det / m_matDet2Src,后续坐标换算都走这对矩阵*/
cv::Mat image = m_CheckResult_shareP->in_shareImage->img;
cv::Rect imgRect(0, 0, image.cols, image.rows);
// 1、旋转矩形四角 -> 轴对齐矩形四角 的仿射(把产品摆正;用顶点计算,避开 RotatedRect 角度约定的歧义)
std::vector<cv::Point2f> vtx = CheckUtil::sort_vertices(m_outer_rroi); // 近似 左上/右上/左下/右下
int detW = std::max(1, cvRound(cv::norm(vtx[1] - vtx[0])));
int detH = std::max(1, cvRound(cv::norm(vtx[2] - vtx[0])));
cv::Point2f srcTri[3] = {vtx[0], vtx[1], vtx[2]};
cv::Point2f dstTri[3] = {cv::Point2f(0, 0), cv::Point2f((float)detW, 0), cv::Point2f(0, (float)detH)};
m_matSrc2Det = cv::getAffineTransform(srcTri, dstTri);
cv::invertAffineTransform(m_matSrc2Det, m_matDet2Src);
// 2、只对旋转矩形附近区域做 warp,提高效率;先裁到该区域,warp 矩阵同步做平移补偿
cv::Rect padRoi = m_outer_rroi.boundingRect() & imgRect;
if (padRoi.width <= 0 || padRoi.height <= 0)
{
m_pdetlog->AddCheckstr(PrintLevel_1, "Error", "deskew roi error [%d %d %d %d]", padRoi.x, padRoi.y, padRoi.width, padRoi.height);
m_nErrorCode = CHECK_ERROR_CheckImg_Empty;
m_nCheckResultErrorCode = m_nErrorCode;
return m_nErrorCode;
}
cv::Mat matPad = m_matSrc2Det.clone();
matPad.at<double>(0, 2) += matPad.at<double>(0, 0) * padRoi.x + matPad.at<double>(0, 1) * padRoi.y;
matPad.at<double>(1, 2) += matPad.at<double>(1, 0) * padRoi.x + matPad.at<double>(1, 1) * padRoi.y;
/*生成 检测的图片(检测用图固定为单通道灰度;彩色原图按 BGR 顺序转灰度,与 imread(path,0) 的亮度一致)*/
cv::Mat image;
image = m_CheckResult_shareP->in_shareImage->img;
if (image.channels() == 3)
// 3、检测图:单通道灰度(彩色原图按 BGR 顺序转灰度,与 imread(path,0) 的亮度一致),按摆正矩阵重采样;
// 彩色结果图在 ResizeImg 里按显示尺寸直接 warp(比全分辨率彩色 warp 省很多)
m_deskewPadRoi = padRoi;
cv::Mat colorPatch = image(padRoi);
cv::Mat grayPatch;
if (colorPatch.channels() == 1)
{
cv::cvtColor(image(m_outer_roi), m_pImageAllResult->detImg, cv::COLOR_BGR2GRAY);
grayPatch = colorPatch;
}
else if (image.channels() == 4)
else if (colorPatch.channels() == 3)
{
cv::cvtColor(image(m_outer_roi), m_pImageAllResult->detImg, cv::COLOR_BGRA2GRAY);
cv::cvtColor(colorPatch, grayPatch, cv::COLOR_BGR2GRAY);
}
else
{
m_pImageAllResult->detImg = image(m_outer_roi).clone();
cv::cvtColor(colorPatch, grayPatch, cv::COLOR_BGRA2GRAY);
}
cv::warpAffine(grayPatch, m_pImageAllResult->detImg, matPad, cv::Size(detW, detH), cv::INTER_LINEAR, cv::BORDER_REPLICATE);
// 4、检测图坐标系下 m_outer_roi 即整张检测图;原图坐标系下 CutRoi 仍取旋转矩形外接区域(兼容旧语义/JSON)
m_outer_roi = cv::Rect(0, 0, detW, detH);
m_Crop_Roi_paramImg = m_outer_rroi.boundingRect() & imgRect;
m_pImageAllResult->pDetResult->CutRoi = m_Crop_Roi_paramImg;
m_pImageAllResult->pDetResult->Param_CropRoi = m_Crop_Roi_paramImg;
// 检测图坐标 -> 原图坐标 的映射,供结果解析时取回缺陷小图/换算坐标
m_pImageAllResult->pDetResult->detToSrcMat = m_matDet2Src.clone();
// 5、参数(模板)坐标 -> 检测图坐标 的映射:
// 有对齐时先把参数点映射到原图再摆正;无对齐时参数按产品坐标系处理(与摆正后的检测图同向),
// 直接按旋转矩形中心平移(θ=0 时与旧的裁剪平移完全一致,旋转/平移变化时区域跟随产品)
{
cv::RotatedRect &rroi = m_outer_rroi;
if (m_pEdge_Align_Result && m_pEdge_Align_Result->buseOfft)
{
if (!m_pEdge_Align_Result->H.empty())
{
m_matParam2Det = MatAffineCompose(m_matSrc2Det, m_pEdge_Align_Result->H);
}
else
{
cv::Mat matOfft = (cv::Mat_<double>(2, 3) << 1, 0, (double)m_pEdge_Align_Result->offt_x, 0, 1, (double)m_pEdge_Align_Result->offt_y);
m_matParam2Det = MatAffineCompose(m_matSrc2Det, matOfft);
}
}
else
{
m_matParam2Det = (cv::Mat_<double>(2, 3) << 1, 0, -rroi.center.x + detW * 0.5, 0, 1, -rroi.center.y + detH * 0.5);
}
}
long time_calan_e = CheckUtil::getcurTime();
m_pdetlog->AddCheckstr(PrintLevel_0, "2.1、pre detect", "-------------------------deskew--------------succ--- det [%d %d] src bbox [%d %d %d %d] time %ld----\n",
detW, detH, m_Crop_Roi_paramImg.x, m_Crop_Roi_paramImg.y, m_Crop_Roi_paramImg.width, m_Crop_Roi_paramImg.height,
time_calan_e - time_edge_s);
/*多线程开启 AI 推理检测*/
long time_AI_s = CheckUtil::getcurTime();
m_AItask = std::make_shared<TaskInfo>();
@ -560,10 +674,8 @@ int ImgCheckAnalysisy::CheckRun()
QX_ERROR_INFO_ temerror;
temerror.Idx = m_pDetResult->pQx_ErrorList->size();
// roi 在原图坐标系,转换为检测图(detImg)坐标系
cv::Rect roi = inner.boundingRect();
roi.x -= m_outer_roi.x;
roi.y -= m_outer_roi.y;
// roi 在原图坐标系,按摆正仿射转换到检测图(detImg)坐标系
cv::Rect roi = MatAffineRect(m_matSrc2Det, inner.boundingRect());
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
temerror.roi = roi;
temerror.area = roi.width * roi.height;
@ -623,10 +735,8 @@ int ImgCheckAnalysisy::CheckRun()
continue;
}
// 上报该把手区域的位置:原图坐标 -> 检测图(detImg)坐标
cv::Rect roi = regionResult.boxRegion;
roi.x -= m_outer_roi.x;
roi.y -= m_outer_roi.y;
// 上报该把手区域的位置:参数(模板)坐标 -> 检测图(detImg)坐标
cv::Rect roi = MatAffineRect(m_matParam2Det, regionResult.boxRegion);
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
if (roi.width <= 0 || roi.height <= 0)
{
@ -678,10 +788,8 @@ int ImgCheckAnalysisy::CheckRun()
{
for (const auto &tagRoi : m_tag_roiList)
{
// tag roi 在原图坐标系,转换为检测图(detImg)坐标系
cv::Rect roi = tagRoi.boundingRect();
roi.x -= m_outer_roi.x;
roi.y -= m_outer_roi.y;
// tag roi 在原图坐标系,按摆正仿射转换到检测图(detImg)坐标系
cv::Rect roi = MatAffineRect(m_matSrc2Det, tagRoi.boundingRect());
roi &= cv::Rect(0, 0, m_pImageAllResult->detImg.cols, m_pImageAllResult->detImg.rows);
if (roi.width <= 0 || roi.height <= 0)
{
@ -1786,21 +1894,32 @@ int ImgCheckAnalysisy::ResizeImg()
float fw = RESIZE_IMAGE_WIDTH * 1.0f / m_pImageAllResult->detImg.cols;
sz.height = int(m_pImageAllResult->detImg.rows * fw);
// 结果图画布取自"原图裁剪区",保持原图通道(彩色原图 -> 真实彩色,不再灰度转假彩);
// ReJson 复测模式下 in_shareImage->img 本身就是裁剪图,直接用检测图兜底
cv::Mat srcCanvas;
// 结果图画布在显示尺寸上按摆正矩阵直接从原图重采样(保持原图通道 -> 真实彩色,不再灰度转假彩);
// 异常情况(如未走摆正流程)直接用检测图兜底
const cv::Mat &srcImg = m_CheckResult_shareP->in_shareImage->img;
if (m_CheckResult_shareP->in_shareImage->Det_Mode != DET_MODE_ReJson &&
!srcImg.empty() && CheckUtil::RoiInImg(m_outer_roi, srcImg))
{
srcCanvas = srcImg(m_outer_roi);
!srcImg.empty() && !m_matSrc2Det.empty() &&
m_deskewPadRoi.width > 0 && m_deskewPadRoi.height > 0 &&
CheckUtil::RoiInImg(m_deskewPadRoi, srcImg))
{
// M_disp = 显示缩放 ∘ M_src2det,并把输入平移到原图裁剪区(padRoi)的局部坐标
cv::Mat matDisp = m_matSrc2Det.clone();
double fsx = sz.width * 1.0 / m_pImageAllResult->detImg.cols;
double fsy = sz.height * 1.0 / m_pImageAllResult->detImg.rows;
matDisp.at<double>(0, 0) *= fsx;
matDisp.at<double>(0, 1) *= fsx;
matDisp.at<double>(0, 2) *= fsx;
matDisp.at<double>(1, 0) *= fsy;
matDisp.at<double>(1, 1) *= fsy;
matDisp.at<double>(1, 2) *= fsy;
matDisp.at<double>(0, 2) += matDisp.at<double>(0, 0) * m_deskewPadRoi.x + matDisp.at<double>(0, 1) * m_deskewPadRoi.y;
matDisp.at<double>(1, 2) += matDisp.at<double>(1, 0) * m_deskewPadRoi.x + matDisp.at<double>(1, 1) * m_deskewPadRoi.y;
cv::warpAffine(srcImg(m_deskewPadRoi), m_pImageAllResult->resultImg, matDisp, sz, cv::INTER_LINEAR, cv::BORDER_REPLICATE);
}
else
{
srcCanvas = m_pImageAllResult->detImg;
cv::resize(m_pImageAllResult->detImg, m_pImageAllResult->resultImg, sz);
}
cv::resize(srcCanvas, m_pImageAllResult->resultImg, sz);
m_CheckResult_shareP->resultimg = m_pImageAllResult->resultImg;
m_pdetlog->AddCheckstr(PrintLevel_1, "showImg size", " [w %d,h %d]-> show img [w %d,h %d] ",
m_outer_roi.width, m_outer_roi.height, sz.width, sz.height);
@ -1844,34 +1963,10 @@ int ImgCheckAnalysisy::Update_DetRoiList()
float fx = m_pImageAllResult->fscale_detToresult_x;
float fy = m_pImageAllResult->fscale_detToresult_y;
int num = 0;
cv::Rect offsetroi = m_Crop_Roi_paramImg;
if (m_pEdge_Align_Result && m_pEdge_Align_Result->buseOfft)
{
for (const auto &region : m_pCommonAnalysisyConfig->regionConfigArr)
{
if (m_pEdge_Align_Result->H.empty())
{
m_DetRoiList.Update_1(region.basicInfo.pointArry, offsetroi, m_pEdge_Align_Result->offt_x, m_pEdge_Align_Result->offt_y, fx, fy);
}
else
{
m_DetRoiList.Update_Marit(region.basicInfo.pointArry, offsetroi, m_pEdge_Align_Result->H, fx, fy);
}
// m_DetRoiList.Update_1(region.basicInfo.pointArry, offsetroi, m_pEdge_Align_Result->offt_x, m_pEdge_Align_Result->offt_y, fx, fy);
}
}
else
// 参数多边形 -> 检测图(摆正坐标系) 的映射在摆正时已算好(m_matParam2Det)
for (const auto &region : m_pCommonAnalysisyConfig->regionConfigArr)
{
for (const auto &region : m_pCommonAnalysisyConfig->regionConfigArr)
{
m_DetRoiList.Update(region.basicInfo.pointArry, m_Crop_Roi_paramImg, fx, fy);
}
m_DetRoiList.Update_Mat(region.basicInfo.pointArry, m_matParam2Det, fx, fy);
}
m_pdetlog->AddCheckstr(PrintLevel_1, DET_LOG_LEVEL_3, "info", "roiList_Src roi num %ld", m_DetRoiList.roiList_Src.size());
@ -1911,7 +2006,7 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
float fs_resize_y = m_pImageAllResult->resultImg.rows * 1.0f / m_pImageAllResult->detImg.rows;
// 绘制m_inner_rroi(蓝色)
// 绘制m_inner_rroi(蓝色),原图坐标 -> 检测图(摆正) -> 结果图
{
cv::Point2f vertices[4];
m_inner_rroi.points(vertices);
@ -1919,8 +2014,9 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
draw_pts.reserve(4);
for (int i = 0; i < 4; i++)
{
draw_pts.emplace_back(cvRound((vertices[i].x - m_Crop_Roi_paramImg.x) * fs_resize_x),
cvRound((vertices[i].y - m_Crop_Roi_paramImg.y) * fs_resize_y));
cv::Point2f detPt = MatAffinePoint(m_matSrc2Det, vertices[i]);
draw_pts.emplace_back(cvRound(detPt.x * fs_resize_x),
cvRound(detPt.y * fs_resize_y));
}
cv::polylines(m_CheckResult_shareP->resultimg, draw_pts, true,
CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, cv::Scalar(255, 0, 0)), 1);
@ -1931,9 +2027,11 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
std::vector<cv::Point> product_roi_show;
for (const auto &pt : m_pbaseCheckFunction->markLine.region)
{
// 参数(模板)坐标 -> 检测图(摆正) -> 结果图
cv::Point2f detPt = MatAffinePoint(m_matParam2Det, cv::Point2f((float)pt.x, (float)pt.y));
cv::Point draw_pt;
draw_pt.x = (pt.x - m_Crop_Roi_paramImg.x) * fs_resize_x;
draw_pt.y = (pt.y - m_Crop_Roi_paramImg.y) * fs_resize_y;
draw_pt.x = cvRound(detPt.x * fs_resize_x);
draw_pt.y = cvRound(detPt.y * fs_resize_y);
product_roi_show.push_back(draw_pt);
}
@ -1953,9 +2051,11 @@ int ImgCheckAnalysisy::DrawResult_Step_1()
{
// if (m_pEdge_Align_Result->markresulList[i].status == Mark_Result_Status_OK)
{
const cv::Point &markPt = m_pEdge_Align_Result->markresulList[i].det_Local_DetImg;
cv::Point2f detPt = MatAffinePoint(m_matSrc2Det, cv::Point2f((float)markPt.x, (float)markPt.y));
cv::Point p;
p.x = m_pEdge_Align_Result->markresulList[i].det_Local_DetImg.x * fs_resize_x;
p.y = m_pEdge_Align_Result->markresulList[i].det_Local_DetImg.y * fs_resize_y;
p.x = cvRound(detPt.x * fs_resize_x);
p.y = cvRound(detPt.y * fs_resize_y);
cv::circle(m_CheckResult_shareP->resultimg, p, 5,
CheckUtil::GetDrawColor(m_CheckResult_shareP->resultimg, cv::Scalar(0, 255, 0)));
}

Loading…
Cancel
Save