diff --git a/AlgorithmModule/src/CameraCheckAnalysisy.cpp b/AlgorithmModule/src/CameraCheckAnalysisy.cpp index a624889..f0495d1 100644 --- a/AlgorithmModule/src/CameraCheckAnalysisy.cpp +++ b/AlgorithmModule/src/CameraCheckAnalysisy.cpp @@ -1652,7 +1652,7 @@ int CameraCheckAnalysisy::GetProductFirstRow(const cv::Mat &img, int &outRow) } const uchar brightThreshold = 35; // 亮像素灰度阈值(与背景判断阈值保持一致) - const int needBrightCount = std::max(1, (int)(gray.cols * 0.2)); // 该行亮像素数量占比超过 20% 认为是产品所在行 + const int needBrightCount = std::max(1, (int)(gray.cols * 0.02)); // 该行亮像素数量占比超过 20% 认为是产品所在行 const int rowStep = 16; // 跳行扫描步长 // 判断某一行是否为产品所在行(亮像素数量达到阈值) diff --git a/example/Read_Image.cpp b/example/Read_Image.cpp index e6316fc..970e59a 100644 --- a/example/Read_Image.cpp +++ b/example/Read_Image.cpp @@ -8,6 +8,65 @@ std::string toLower(const std::string &str) return lower; } +// 4 相机代码, 顺序与原有实现保持一致 +static const std::string g_strCameraCodes[] = {"BCA", "DCA", "BTA", "DTA"}; + +// 解析文件名中的 相机代码 与 半幅(左右) +// 支持两种命名: +// 1) {前缀}_{相机}_{0|1}_{后缀} 如 3ABD670013A8BG1_BCA_0_src (0=左半, 1=右半) +// 2) {前缀}{相机}{A|B} 如 BCAA / A_BCAB (A=左半, B=右半) +// 对应关系: BCA_0 <=> BCAA(左半), BCA_1 <=> BCAB(右半) +std::string Extract_Camera_And_Half(const std::string &strStem, std::string &strHalf) +{ + strHalf = ""; + + // 方式1: {前缀}_{相机}_{0|1}_{后缀} (merg 目录下的命名) + // 形如 3ABD670013A8BG1_BCA_0_src.jpg, 其中 0 对应 BCAA, 1 对应 BCAB + for (const auto &strCam : g_strCameraCodes) + { + const std::string strKey = "_" + strCam + "_"; + size_t pos = strStem.find(strKey); + while (pos != std::string::npos) + { + const size_t nHalfPos = pos + strKey.size(); + if (nHalfPos < strStem.size() && + (strStem[nHalfPos] == '0' || strStem[nHalfPos] == '1') && + (nHalfPos + 1 >= strStem.size() || strStem[nHalfPos + 1] == '_' || strStem[nHalfPos + 1] == '.')) + { + strHalf = (strStem[nHalfPos] == '0') ? "A" : "B"; + return strCam; + } + pos = strStem.find(strKey, pos + 1); + } + } + + // 方式2: {前缀}{相机}{A|B} (原有的 4 相机命名) + for (const auto &strCam : g_strCameraCodes) + { + const size_t pos = strStem.find(strCam); + if (pos != std::string::npos && pos + strCam.size() < strStem.size()) + { + const char cHalf = strStem[pos + strCam.size()]; + if (cHalf == 'A' || cHalf == 'B') + { + strHalf = std::string(1, cHalf); + return strCam; + } + } + } + + // 兜底: 文件名中只包含相机代码, 无法判定左右 + for (const auto &strCam : g_strCameraCodes) + { + if (strStem.find(strCam) != std::string::npos) + { + return strCam; + } + } + + return ""; +} + int Extract_Name_Base::Read_Product_Image(std::string strPath) { @@ -189,12 +248,17 @@ int Extract_ALL::Extract_Info(std::string strPath, std::string strproduct, int u fs::path p = fs::path(strPath); std::string filenameWithoutExt = p.stem().string(); + // 记录半幅信息: 左半(A/BCAA/…_0_) / 右半(B/BCAB/…_1_), 用于配对时确定左右图 + std::string strHalf; + Extract_Camera_And_Half(filenameWithoutExt, strHalf); + std::shared_ptr pImage = std::make_shared(); pImage->strProductID = strProductName; pImage->strCamID = strCameraName; pImage->strchannelName = strChannelName; pImage->strName = filenameWithoutExt; + pImage->strHalf = strHalf; pImage->strPath = strPath; pCamera->image_list.push_back(pImage); @@ -207,17 +271,10 @@ std::string Extract_ALL::Extract_Camera_Name(std::string strPath, int userflag) fs::path p = fs::path(strPath); std::string filename = p.stem(); - // 文件名包含 BCA/BTA/DCA/DTA/CA/TA 即判定对应相机 - if (filename.find("BCA") != std::string::npos) - return "BCA"; - else if (filename.find("DCA") != std::string::npos) - return "DCA"; - else if (filename.find("BTA") != std::string::npos) - return "BTA"; - else if (filename.find("DTA") != std::string::npos) - return "DTA"; - - return ""; + // 相机名从统一的命名解析中获取: + // {相机}{A|B} 形式(BCAA/BCAB) 与 {相机}_{0|1} 形式(BCA_0/BCA_1) 均支持 + std::string strHalf; + return Extract_Camera_And_Half(filename, strHalf); } std::string Extract_ALL::Extract_Product_Name(std::string strPath, int userflag) @@ -252,6 +309,7 @@ std::string Extract_ALL::Extract_Channel_Name(std::string strPath, int userflag) // 新格式: {Prefix}_{Camera}{AB}, 如 A_BCAA, M_DTAB, A_BCAB // 通道名 = 前缀 (A/M), 表示不同打光方式 // 支持4相机: BCAA, BCAB, BTAA, BTAB, DCAA, DCAB, DTAA, DTAB + // 兼容 {Camera}_{0|1} 命名(如 BCA_0 / BCA_1), 其 0/1 对应 A/B size_t underscorePos = filename.find('_'); if (underscorePos != std::string::npos) { @@ -259,7 +317,9 @@ std::string Extract_ALL::Extract_Channel_Name(std::string strPath, int userflag) std::string rest = filename.substr(underscorePos + 1); // 4相机格式 if (rest == "BCAA" || rest == "BCAB" || rest == "BTAA" || rest == "BTAB" || - rest == "DCAA" || rest == "DCAB" || rest == "DTAA" || rest == "DTAB" ) + rest == "DCAA" || rest == "DCAB" || rest == "DTAA" || rest == "DTAB" || + rest == "BCA_0" || rest == "BCA_1" || rest == "BTA_0" || rest == "BTA_1" || + rest == "DCA_0" || rest == "DCA_1" || rest == "DTA_0" || rest == "DTA_1") { return prefix; // "A" or "M" } diff --git a/example/Read_Image.h b/example/Read_Image.h index 0265f62..00a36a7 100644 --- a/example/Read_Image.h +++ b/example/Read_Image.h @@ -15,6 +15,20 @@ namespace fs = std::filesystem; using namespace std; +// ==================== 图片命名解析 ==================== +// 相机代码: BCA / BTA / DCA / DTA +// 目前支持两种图片命名方式: +// 1) {前缀}{相机}{A|B} 例: BCAA.jpg / A_BCAB.jpg / M_DTAB.jpg +// 末位 A = 左半, B = 右半 +// 2) {前缀}_{相机}_{0|1}_{后缀} 例: 3ABD670013A8BG1_BCA_0_src.jpg +// 0 = 左半, 1 = 右半 +// 两种命名的对应关系: BCA_0 等价于 BCAA, BCA_1 等价于 BCAB; BTA/DCA/DTA 同理。 +// +// strStem : 去掉目录与扩展名的文件名 +// strHalf : 输出半幅信息, "A"=左半, "B"=右半, 无法判定时为空 +// 返回值 : 相机代码, 未识别到相机时返回空字符串 +std::string Extract_Camera_And_Half(const std::string &strStem, std::string &strHalf); + // 图片信息 struct Image_Info { @@ -23,6 +37,7 @@ struct Image_Info std::string strchannelName; // 通道名称 std::string strPath; // 完整路径 std::string strName; // 图片名称 + std::string strHalf; // 半幅: "A"=左半(BCAA/…_0_), "B"=右半(BCAB/…_1_) cv::Mat img; // 图像数据 long readImg_start; // 读取开始时间 long readImg_end; // 读取结束时间 @@ -41,6 +56,7 @@ struct Image_Info strchannelName = ""; strPath = ""; strName = ""; + strHalf = ""; img.release(); // 释放图像内存 readImg_start = 0; readImg_end = 0; @@ -53,6 +69,7 @@ struct Image_Info std::cout << "Channel Name: " << strchannelName << std::endl; std::cout << "Image Path: " << strPath << std::endl; std::cout << "Image Name: " << strName << std::endl; + std::cout << "Image Half: " << strHalf << std::endl; std::cout << "Read Start Time: " << readImg_start << std::endl; std::cout << "Read End Time: " << readImg_end << std::endl; } diff --git a/example/deal.cpp b/example/deal.cpp index fad1a3b..63032d6 100644 --- a/example/deal.cpp +++ b/example/deal.cpp @@ -556,19 +556,26 @@ int deal::preCheck() for (const auto pcam : product->camera_list) { auto& imgList = pcam->image_list; - // 同一camera内按配对处理:每两张图一组 (CAA+CAB / TAA+TAB) - // cv::glob 返回字母序,确保 A_CAA 在 A_CAB 前,M_CAA 在 M_CAB 前 + // 同一camera内按配对处理:每两张图一组 (左半 + 右半) + // 支持 {相机}{A|B}(BCAA/BCAB) 与 {相机}_{0|1}(BCA_0/BCA_1, merg 目录) 两种命名 + // 左半(A/…_0_)为左图, 右半(B/…_1_)为右图; 若读入顺序相反则交换 for (size_t i = 0; i < imgList.size(); i += 2) { + std::shared_ptr pLeft = imgList[i]; + std::shared_ptr pRight = (i + 1 < imgList.size()) ? imgList[i + 1] : nullptr; + if (pRight && pLeft->strHalf == "B" && pRight->strHalf == "A") + { + std::swap(pLeft, pRight); + } std::shared_ptr tem = std::make_shared(); tem->strCamID = pcam->strCamName; - tem->strchannelName = imgList[i]->strchannelName; - tem->strName = imgList[i]->strName; - tem->strProductID = imgList[i]->strProductID; - tem->strPath = imgList[i]->strPath; - if (i + 1 < imgList.size()) + tem->strchannelName = pLeft->strchannelName; + tem->strName = pLeft->strName; + tem->strProductID = pLeft->strProductID; + tem->strPath = pLeft->strPath; + if (pRight) { - tem->strPath_B = imgList[i + 1]->strPath; + tem->strPath_B = pRight->strPath; } InsertReadImgInfo(tem); AllImgNum++; @@ -1340,30 +1347,15 @@ int deal::GetImgInfo_POL_ET(std::string strImgPath, JC_IMAGE_INFO_ *pImageInfo) strName = ExtractFileNameWithoutExtension(strImgPath); printf("strName %s \n", strName.c_str()); - std::string strImageChannel; - - // 使用 find 检查是否包含 4 个相机标识 (BCA, BTA, DCA, DTA) 及兼容旧 CA/TA - if (strName.find("BCA") != std::string::npos) - { - strImageChannel = "BCA"; - } - else if (strName.find("BTA") != std::string::npos) - { - strImageChannel = "BTA"; - } - else if (strName.find("DCA") != std::string::npos) - { - strImageChannel = "DCA"; - } - else if (strName.find("DTA") != std::string::npos) - { - strImageChannel = "DTA"; - } - else + // 统一命名解析: 同时支持 {相机}{A|B}(BCAA/BCAB) 与 {相机}_{0|1}(BCA_0/BCA_1) 命名 + // 对应关系: BCA_0 <=> BCAA, BCA_1 <=> BCAB; BTA/DCA/DTA 同理 + std::string strHalf; + std::string strImageChannel = Extract_Camera_And_Half(strName, strHalf); + if (strImageChannel.empty()) { return 1; } - printf("strImageChannel %s\n", strImageChannel.c_str()); + printf("strImageChannel %s strHalf %s\n", strImageChannel.c_str(), strHalf.c_str()); pImageInfo->strchannelName = strImageChannel; pImageInfo->strName = strName; pImageInfo->strPath = strImgPath; @@ -2482,7 +2474,8 @@ int deal::Det_OneProduct_Cell_ET(std::string strProductName, int Idx) int AllImgNum = detImgInfoList.size(); // 遍历所有图片 开始读图处理 - // 按相机分组,同相机内每两张配对 (BCAA+BCAB 拼接送检,与 preCheck 一致) + // 按相机分组,同相机内每两张配对 (A半+ B半 拼接送检,与 preCheck 一致) + // 注: 除旧的 BCAA/BCAB 命名外,同时支持 merg 目录下的 xxx_BCA_0_src / xxx_BCA_1_src 命名 std::map> camGroups; for (int i = 0; i < (int)detImgInfoList.size(); i++) { @@ -2494,11 +2487,25 @@ int deal::Det_OneProduct_Cell_ET(std::string strProductName, int Idx) auto& indices = kv.second; for (size_t i = 0; i < indices.size(); i += 2) { + int nLeftIdx = indices[i]; + int nRightIdx = (i + 1 < indices.size()) ? indices[i + 1] : -1; + // 左右图由文件名决定: A半(BCAA/…_0_)为左图, B半(BCAB/…_1_)为右图; 顺序相反时交换 + if (nRightIdx >= 0) + { + std::string strHalfL; + std::string strHalfR; + Extract_Camera_And_Half(detImgInfoList.at(nLeftIdx).strName, strHalfL); + Extract_Camera_And_Half(detImgInfoList.at(nRightIdx).strName, strHalfR); + if (strHalfL == "B" && strHalfR == "A") + { + std::swap(nLeftIdx, nRightIdx); + } + } std::shared_ptr tem = std::make_shared(); - tem->copy(detImgInfoList.at(indices[i])); - if (i + 1 < indices.size()) + tem->copy(detImgInfoList.at(nLeftIdx)); + if (nRightIdx >= 0) { - tem->strPath_B = detImgInfoList.at(indices[i + 1]).strPath; + tem->strPath_B = detImgInfoList.at(nRightIdx).strPath; } InsertReadImgInfo(tem); AllImgNum++;