diff --git a/AlgorithmModule/include/ImageAllResult.h b/AlgorithmModule/include/ImageAllResult.h index a6e59b2..af8f3c7 100644 --- a/AlgorithmModule/include/ImageAllResult.h +++ b/AlgorithmModule/include/ImageAllResult.h @@ -62,6 +62,8 @@ public: cv::Rect CropRoi = cv::Rect(0, 0, 0, 0); // 检测的原始图片 cv::Mat detImg; + // 摆正后的彩色检测图(保留原图通道,供裁剪图 _RE_CutImg 输出;灰度源图时为空) + cv::Mat detColorImg; // AI 推理的mask图片 cv::Mat AIMaskImg; // 结果图片 diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 9e195b4..b0223fd 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -480,6 +480,63 @@ int ImgCheckAnalysisy::Adapt_Config(RotatedRect tplOuterRect, RotatedRect curOut return 0; } +// 应用内 OpenCV 线程池随首个调用线程(通常为先绑核的检测线程)初始化成单线程,parallel_for 实际串行, +// 全分辨率 warp 要 150~200ms;这里把输出按行切条、用独立线程并行 warp, +// 结果与整图 warpAffine 逐像素一致(已离线验证 maxdiff=0),耗时降到约 1/3 +static void WarpAffineBandParallel(const cv::Mat &src, cv::Mat &dst, const cv::Mat &affine, + const cv::Size &size, int nThreads = 8, int interp = cv::INTER_LINEAR, + int borderType = cv::BORDER_REPLICATE) +{ + dst.create(size, src.type()); + nThreads = std::max(1, std::min(nThreads, size.height)); + int bandH = (size.height + nThreads - 1) / nThreads; + int nBands = (size.height + bandH - 1) / bandH; + + auto warpBand = [&](int b) + { + // 新建线程默认继承调用线程的 CPU 亲和性(检测线程已绑单核),这里放开到全核,让条带真正并行 + cpu_set_t cpus; + CPU_ZERO(&cpus); + long ncpu = sysconf(_SC_NPROCESSORS_ONLN); + for (long i = 0; i < ncpu; i++) + { + CPU_SET(i, &cpus); + } + pthread_setaffinity_np(pthread_self(), sizeof(cpus), &cpus); + + int y0 = b * bandH; + int h = std::min(bandH, size.height - y0); + // warpAffine 默认接收正向矩阵(src->dst)并内部求逆;条带只把输出平移项减去 y0 + cv::Mat matBand = affine.clone(); + matBand.at(1, 2) -= y0; + cv::warpAffine(src, dst(cv::Rect(0, y0, size.width, h)), matBand, + cv::Size(size.width, h), interp, borderType); + }; + + std::vector threads; + threads.reserve(nBands); + int b = 0; + try + { + for (; b < nBands; b++) + { + threads.emplace_back(warpBand, b); + } + } + catch (const std::system_error &) + { + // 线程资源不足:剩余条带留在本线程串行补完 + } + for (auto &t : threads) + { + t.join(); + } + for (; b < nBands; b++) + { + warpBand(b); + } +} + int ImgCheckAnalysisy::CheckRun() { @@ -596,7 +653,18 @@ int ImgCheckAnalysisy::CheckRun() { cv::cvtColor(colorPatch, grayPatch, cv::COLOR_BGRA2GRAY); } - cv::warpAffine(grayPatch, m_pImageAllResult->detImg, matPad, cv::Size(detW, detH), cv::INTER_LINEAR, cv::BORDER_REPLICATE); + WarpAffineBandParallel(grayPatch, m_pImageAllResult->detImg, matPad, cv::Size(detW, detH)); + + // 彩色原图时再输出一张同样摆正的彩色检测图:供裁剪图(_RE_CutImg)保存时保留原图通道 + // (检测算法仍只用灰度 detImg) + if (colorPatch.channels() == 3 || colorPatch.channels() == 4) + { + WarpAffineBandParallel(colorPatch, m_pImageAllResult->detColorImg, matPad, cv::Size(detW, detH)); + } + else + { + m_pImageAllResult->detColorImg.release(); + } // 4、检测图坐标系下 m_outer_roi 即整张检测图;原图坐标系下 CutRoi 仍取旋转矩形外接区域(兼容旧语义/JSON) m_outer_roi = cv::Rect(0, 0, detW, detH); @@ -2567,7 +2635,10 @@ int ImgCheckAnalysisy::ImgPreDet() creatsavedir(); } - m_CheckResult_shareP->cutSrcimg = m_pImageAllResult->detImg; + // 裁剪图(_RE_CutImg)保留原图通道:源图为彩色时用彩色摆正图,否则回退灰度检测图 + m_CheckResult_shareP->cutSrcimg = m_pImageAllResult->detColorImg.empty() + ? m_pImageAllResult->detImg + : m_pImageAllResult->detColorImg; ResizeImg(); m_pImageAllResult->fscale_detToresult_x = m_pImageAllResult->resultImg.cols * 1.0f / m_pImageAllResult->detImg.cols; diff --git a/example/deal.cpp b/example/deal.cpp index 05b1545..9b35f72 100644 --- a/example/deal.cpp +++ b/example/deal.cpp @@ -1016,16 +1016,13 @@ int deal::SendImgToCheck(std::shared_ptr pDetImageInfo, IN_IMG_S } if (status == IN_IMG_Status_End) { - // 一张图上下拆分成两个产品(_0 上半、_1 下半),给两个产品分别发送结束标志 - for (int pi = 0; pi < 2; pi++) + // 不再上下拆图:一张图对应一个产品,只给该产品发送一次结束标志 + std::shared_ptr tem = std::make_shared(); + tem->strImgProductID = pDetImageInfo->strProductID; + tem->Status = -1; + if (m_pALLImgCheckAnalysisy) { - std::shared_ptr tem = std::make_shared(); - tem->strImgProductID = pDetImageInfo->strProductID + "_" + std::to_string(pi); - tem->Status = -1; - if (m_pALLImgCheckAnalysisy) - { - m_pALLImgCheckAnalysisy->SetDataRun_SharePtr(tem); - } + m_pALLImgCheckAnalysisy->SetDataRun_SharePtr(tem); } return 0; } @@ -1079,38 +1076,7 @@ int deal::SendImgToCheck(std::shared_ptr pDetImageInfo, IN_IMG_S int re = 0; - // 一张图拍多个产品:上下拆分成多张,子图产品ID加索引后缀,分别送检 - std::vector splitList; - if (!pDetImageInfo->img.empty() && pDetImageInfo->img.rows >= 2) - { - int half = pDetImageInfo->img.rows / 2; - splitList.push_back(pDetImageInfo->img(cv::Rect(0, 0, pDetImageInfo->img.cols, half)).clone()); - splitList.push_back(pDetImageInfo->img(cv::Rect(0, half, pDetImageInfo->img.cols, pDetImageInfo->img.rows - half)).clone()); - } - - if (splitList.size() > 1) - { - for (size_t i = 0; i < splitList.size(); i++) - { - std::shared_ptr sub = std::make_shared(); - *sub = *tem; - sub->img = splitList[i]; - sub->strImgProductID = tem->strImgProductID + "_" + std::to_string(i); - // 同一产品(上半/下半)可能包含多个通道的图,子图作为中间图,待所有通道送完后再统一发送结束标志 - sub->Status = IN_IMG_Status_Other; - if (m_pALLImgCheckAnalysisy) - { - re = m_pALLImgCheckAnalysisy->SetDataRun_SharePtr(sub); - if (re != 0) - { - return re; - } - } - m_nPushImgCount++; - } - return 0; - } - + // 不再上下拆图:一张图对应一个产品,整图直接送检(产品ID不加索引后缀) if (m_pALLImgCheckAnalysisy) { re = m_pALLImgCheckAnalysisy->SetDataRun_SharePtr(tem);