diff --git a/AlgorithmModule/include/CameraCheckAnalysisy.hpp b/AlgorithmModule/include/CameraCheckAnalysisy.hpp index 5321767..f566b4a 100644 --- a/AlgorithmModule/include/CameraCheckAnalysisy.hpp +++ b/AlgorithmModule/include/CameraCheckAnalysisy.hpp @@ -188,6 +188,9 @@ private: int GetLine(const cv::Mat &img, std::vector &pointList, int lineNum, int xory, int &outP); + // 找到产品(亮色区域)所在的第一行(顶部边缘), 用于左右图上下对齐 + int GetProductFirstRow(const cv::Mat &img, int &outRow); + public: // 运行的基本参数 RunInfoST m_RunConfig; diff --git a/AlgorithmModule/src/CameraCheckAnalysisy.cpp b/AlgorithmModule/src/CameraCheckAnalysisy.cpp index 68ef754..e271108 100644 --- a/AlgorithmModule/src/CameraCheckAnalysisy.cpp +++ b/AlgorithmModule/src/CameraCheckAnalysisy.cpp @@ -9,6 +9,7 @@ #include "CameraCheckAnalysisy.hpp" #include "CheckUtil.hpp" #include "Define.h" +#include CameraCheckAnalysisy::CameraCheckAnalysisy() { @@ -174,19 +175,52 @@ int CameraCheckAnalysisy::Detect_Pre() m_pdetlog->AddCheckstr(PrintLevel_1, "Detect_Pre", "Cam %s ==================img size/type != img_B ", strBasic.c_str()); return 1; } + cv::Mat &img = pImageResult->result->in_shareImage->img; + cv::Mat &img_B = pImageResult->result->in_shareImage->img_B; + + // 左右图存在上下错位: 找到左右图产品所在的第一行, 进行简单上下对齐 + { + int rowLeft = -1; + int rowRight = -1; + if (GetProductFirstRow(img, rowLeft) == 0 && GetProductFirstRow(img_B, rowRight) == 0) + { + // 以较高的顶边为基准, 把较低的那张图向上平移, 使两图顶边对齐 + int refRow = std::min(rowLeft, rowRight); + int shiftLeft = refRow - rowLeft; // <= 0, 负值表示向上平移 + int shiftRight = refRow - rowRight; // <= 0 + + m_pdetlog->AddCheckstr(PrintLevel_1, "Detect_Pre", "Cam %s ==================align rowLeft %d rowRight %d shiftLeft %d shiftRight %d", + strBasic.c_str(), rowLeft, rowRight, shiftLeft, shiftRight); + + if (shiftLeft < 0) + { + cv::Mat M = (cv::Mat_(2, 3) << 1, 0, 0, 0, 1, (double)shiftLeft); + cv::warpAffine(img, img, M, img.size(), cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0)); + } + if (shiftRight < 0) + { + cv::Mat M = (cv::Mat_(2, 3) << 1, 0, 0, 0, 1, (double)shiftRight); + cv::warpAffine(img_B, img_B, M, img_B.size(), cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0)); + } + } + else + { + m_pdetlog->AddCheckstr(PrintLevel_1, "Detect_Pre", "Cam %s ==================GetProductFirstRow fail, skip align", strBasic.c_str()); + } + } + const int overlap = 1056; - if (pImageResult->result->in_shareImage->img.cols <= overlap) + if (img.cols <= overlap) { m_pdetlog->AddCheckstr(PrintLevel_1, "Detect_Pre", "Cam %s ==================img.cols <= overlap %d ", strBasic.c_str(), overlap); return 1; } cv::Mat merge_img; - cv::hconcat(pImageResult->result->in_shareImage->img(cv::Rect(0, 0, pImageResult->result->in_shareImage->img.cols - overlap, pImageResult->result->in_shareImage->img.rows)), - pImageResult->result->in_shareImage->img_B, merge_img); - pImageResult->result->in_shareImage->img = merge_img; + cv::hconcat(img(cv::Rect(0, 0, img.cols - overlap, img.rows)), img_B, merge_img); + img = merge_img; // 拼接完成后 img_B 不再需要,立即释放内存 - pImageResult->result->in_shareImage->img_B.release(); + img_B.release(); if (pImageResult->result->in_shareImage->Det_Mode == DET_MODE_MergeImg) { @@ -1519,3 +1553,79 @@ int CameraCheckAnalysisy::GetLine(const cv::Mat &img, std::vector &po return 0; } + +// 找到图像中产品(亮色区域)所在的第一行(顶部边缘), 用于左右图上下对齐 +// 从上往下逐行扫描, 当某一行亮像素数量超过阈值比例时, 认为该行是产品顶边 +int CameraCheckAnalysisy::GetProductFirstRow(const cv::Mat &img, int &outRow) +{ + outRow = -1; + if (img.empty()) + { + return 1; + } + + cv::Mat gray; + if (img.channels() == 1) + { + gray = img; + } + else if (img.channels() == 3) + { + cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY); + } + else + { + return 1; + } + + const uchar brightThreshold = 35; // 亮像素灰度阈值(与背景判断阈值保持一致) + const int needBrightCount = std::max(1, (int)(gray.cols * 0.2)); // 该行亮像素数量占比超过 20% 认为是产品所在行 + const int rowStep = 16; // 跳行扫描步长 + + // 判断某一行是否为产品所在行(亮像素数量达到阈值) + auto isProductRow = [&](int row) -> bool + { + const uchar *p = gray.ptr(row); + int brightCount = 0; + for (int col = 0; col < gray.cols; ++col) + { + if (p[col] >= brightThreshold) + { + if (++brightCount >= needBrightCount) + { + return true; + } + } + } + return false; + }; + + // 1、跳行粗扫: 每隔 rowStep 行扫描一次, 定位产品顶边的大致位置 + int rowFound = -1; + for (int row = 0; row < gray.rows; row += rowStep) + { + if (isProductRow(row)) + { + rowFound = row; + break; + } + } + + if (rowFound < 0) + { + return 2; // 未找到产品顶边 + } + + // 2、在上一个采样行与 rowFound 之间逐行精确定位 + int rowStart = std::max(0, rowFound - rowStep + 1); + for (int row = rowStart; row <= rowFound; ++row) + { + if (isProductRow(row)) + { + outRow = row; + return 0; + } + } + + return 2; // 理论上不会走到这里 +}