#include #include #include #include #pragma once using namespace HalconCpp; struct MatchResult { cv::Rect boundingRect; double confidence; }; // 简单的图像相关算法 //融合两张图片白色区域 static cv::Mat mergeBinaryMats(const cv::Mat& mat1, const cv::Mat& mat2) { // 确保两个输入图像具有相同的尺寸 if (mat1.size() != mat2.size()) { std::cerr << "Error: Input images must have the same size." << std::endl; return cv::Mat(); } // 创建一个输出图像,初始化为全黑 cv::Mat merged = cv::Mat::zeros(mat1.size(), CV_8U); // 将两个输入图像中的白色部分合并到输出图像 cv::bitwise_or(mat1, mat2, merged); return merged; } //Rect相加 static cv::Rect rectPlusRect(cv::Rect world, cv::Rect rect) { return cv::Rect(rect.x + world.x, rect.y + world.y, rect.width, rect.height); } static cv::Rect findMaxAreaContour(cv::Mat src) { int maxIndex = 0, maxArea = 0; std::vector>contours; cv::findContours(src, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); if (contours.size() == 0) return cv::Rect(0, 0, 0, 0); for (int i = 0; i < contours.size(); i++) { int Area = cv::contourArea(contours[i]); if (Area > maxArea) { maxArea = Area; maxIndex = i; } } if (contours.size() != 0) return cv::boundingRect(contours[maxIndex]); return cv::Rect(0, 0, 0, 0); } static cv::Rect findMaxAreaContour(cv::Mat src, int& indexReturn) { int maxIndex = 0, maxArea = 0; std::vector>contours; cv::findContours(src, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); if (contours.size() == 0) return cv::Rect(0, 0, 0, 0); for (int i = 0; i < contours.size(); i++) { int Area = cv::contourArea(contours[i]); if (Area > maxArea) { maxArea = Area; maxIndex = i; } } if (contours.size() != 0) { indexReturn = maxIndex; return cv::boundingRect(contours[maxIndex]); } } static cv::Mat cutImg2Cls(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targetSize = cv::Size(128, 128)); static cv::Mat cutImg2Cls(std::vector contour, cv::Mat imgInput, cv::Size targetSize = cv::Size(128, 128)); static std::vector cutImg2ClsVec(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targetSize = cv::Size(128, 128)) { std::vector result; if (boundingBox.width <= 0 || boundingBox.height <= 0 || imgInput.empty()) { std::cerr << "Invalid bounding box or empty input image." << std::endl; return result; } int minSide = getMin(boundingBox.width, boundingBox.height); if (minSide == 0) { std::cerr << "Bounding box has zero width or height." << std::endl; return result; } int splitCount = getMax(1, getMax(boundingBox.width, boundingBox.height) / minSide); if (splitCount > 2) { if (boundingBox.width > boundingBox.height) { // 横向裁 for (int i = 0; i < splitCount; i++) { int x = boundingBox.x + boundingBox.height * i; if (x + boundingBox.height > boundingBox.x + boundingBox.width) break; result.emplace_back(cutImg2Cls(cv::Rect(x, boundingBox.y, boundingBox.height, boundingBox.height), imgInput, targetSize)); } // 确保最右边包含 result.emplace_back(cutImg2Cls(cv::Rect(boundingBox.x + boundingBox.width - boundingBox.height, boundingBox.y, boundingBox.height, boundingBox.height), imgInput, targetSize)); } else { // 纵向裁 for (int i = 0; i < splitCount; i++) { int y = boundingBox.y + boundingBox.width * i; if (y + boundingBox.width > boundingBox.y + boundingBox.height) break; result.emplace_back(cutImg2Cls(cv::Rect(boundingBox.x, y, boundingBox.width, boundingBox.width), imgInput, targetSize)); } result.emplace_back(cutImg2Cls(cv::Rect(boundingBox.x, boundingBox.y + boundingBox.height - boundingBox.width, boundingBox.width, boundingBox.width), imgInput, targetSize)); } } else { result.emplace_back(cutImg2Cls(boundingBox, imgInput, targetSize)); } return result; } static cv::Mat cutImg2Cls(std::vector contour, cv::Mat imgInput, cv::Size targetSize) { if (contour.empty() || imgInput.empty()) { std::cerr << "Empty contour or input image." << std::endl; return cv::Mat(); } cv::Rect boundingBox = cv::boundingRect(contour); return cutImg2Cls(boundingBox, imgInput, targetSize); } static cv::Mat cutImg2Cls(cv::Rect boundingBox, cv::Mat imgInput, cv::Size targetSize) { if (boundingBox.width <= 0 || boundingBox.height <= 0 || imgInput.empty()) { std::cerr << "Invalid bounding box or empty input image." << std::endl; return cv::Mat(); } cv::Point mid(boundingBox.x + boundingBox.width / 2, boundingBox.y + boundingBox.height / 2); int sideLength = getMax(getMax(boundingBox.width, boundingBox.height), 128); // 生成一个空的正方形黑图 cv::Mat squareImage(sideLength, sideLength, imgInput.type(), cv::Scalar(0, 0, 0)); // 从原图中取的期望矩形区域 cv::Rect expectRect(mid.x - sideLength / 2, mid.y - sideLength / 2, sideLength, sideLength); int xOffset = 0; int yOffset = 0; // 调整 expectRect 防止越界 if (expectRect.x < 0) { expectRect.width += expectRect.x; xOffset -= expectRect.x; expectRect.x = 0; } if (expectRect.y < 0) { expectRect.height += expectRect.y; yOffset -= expectRect.y; expectRect.y = 0; } if (expectRect.x + expectRect.width > imgInput.cols) expectRect.width = imgInput.cols - expectRect.x; if (expectRect.y + expectRect.height > imgInput.rows) expectRect.height = imgInput.rows - expectRect.y; if (expectRect.width <= 0 || expectRect.height <= 0) { std::cerr << "Adjusted region is invalid after clipping." << std::endl; return cv::Mat(); } imgInput(expectRect).copyTo(squareImage(cv::Rect(xOffset, yOffset, expectRect.width, expectRect.height))); cv::resize(squareImage, squareImage, targetSize); return squareImage; } static cv::Mat HObject2Mat(HObject& Hobj) //HObject类型转Mat类型 { HTuple htCh; HString cType; cv::Mat Image; ConvertImageType(Hobj, &Hobj, "byte"); CountChannels(Hobj, &htCh); Hlong wid = 0; Hlong hgt = 0; if (htCh[0].I() == 1) { HImage hImg(Hobj); void* ptr = hImg.GetImagePointer1(&cType, &wid, &hgt);//GetImagePointer1(Hobj, &ptr, &cType, &wid, &hgt); int W = wid; int H = hgt; Image.create(H, W, CV_8UC1); unsigned char* pdata = static_cast(ptr); memcpy(Image.data, pdata, W * H); } else if (htCh[0].I() == 3) { void* Rptr; void* Gptr; void* Bptr; HImage hImg(Hobj); hImg.GetImagePointer3(&Rptr, &Gptr, &Bptr, &cType, &wid, &hgt); int W = wid; int H = hgt; Image.create(H, W, CV_8UC3); std::vector VecM(3); VecM[0].create(H, W, CV_8UC1); VecM[1].create(H, W, CV_8UC1); VecM[2].create(H, W, CV_8UC1); unsigned char* R2 = (unsigned char*)Rptr; unsigned char* G = (unsigned char*)Gptr; unsigned char* B = (unsigned char*)Bptr; memcpy(VecM[2].data, R2, W * H); memcpy(VecM[1].data, G, W * H); memcpy(VecM[0].data, B, W * H); cv::merge(VecM, Image); } return Image; } enum MatsToHObjectEnum { horizontal, vertical }; static HalconCpp::HObject MatsToHObject(cv::Mat* img1, cv::Mat* img2, MatsToHObjectEnum type) { auto start3 = std::chrono::high_resolution_clock::now(); HalconCpp::HObject Hobj; int height = 0; int width = 0; switch (type) { case horizontal: width = img1->cols; height = img1->rows + img2->rows; break; case vertical: width = img1->cols + img2->cols; height = img1->rows; break; } if (img1->type() == CV_8UC1) { unsigned char* imgData = new unsigned char[width * height]; memcpy(imgData, img1->data, img1->rows * img1->cols); memcpy(img1->rows * img1->cols + imgData, img2->data, img1->rows * img1->cols); GenImage1(&Hobj, "byte", width, height, (Hlong)imgData); delete[]imgData; imgData = nullptr; } auto end3 = std::chrono::high_resolution_clock::now(); auto duration3 = std::chrono::duration_cast(end3 - start3); std::cout << "Time taken by functionxxx: " << duration3.count() << " milliseconds" << std::endl; return Hobj; } static HalconCpp::HObject MatsToHObject(cv::Mat& img1, cv::Mat& img2, MatsToHObjectEnum type) { auto start3 = std::chrono::high_resolution_clock::now(); HalconCpp::HObject Hobj; int height = 0; int width = 0; switch (type) { case horizontal: width = img1.cols; height = img1.rows + img2.rows; break; case vertical: width = img1.cols + img2.cols; height = img1.rows; break; } if (img1.type() == CV_8UC1) { unsigned char* imgData = new unsigned char[width * height]; //#pragma omp parallel for //for (int i = 0; i < 2; i++) //{ // switch (i) // { // case 0: // memcpy(imgData, img1.data, img1.rows * img1.cols); // break; // case 1: // memcpy(img1.rows * img1.cols + imgData, img2.data, img1.rows * img1.cols); // break; // } //} memcpy(imgData, img1.data, img1.rows * img1.cols); memcpy(img1.rows * img1.cols + imgData, img2.data, img1.rows * img1.cols); GenImage1(&Hobj, "byte", width, height, (Hlong)imgData); delete[]imgData; imgData = nullptr; } auto end3 = std::chrono::high_resolution_clock::now(); auto duration3 = std::chrono::duration_cast(end3 - start3); std::cout << "Time taken by functionxxx: " << duration3.count() << " milliseconds" << std::endl; return Hobj; } static void MatToHObjectV(cv::Mat& image, HalconCpp::HObject* Hobj) { int hgt = image.rows; int wid = image.cols; int i; if (image.type() == CV_8UC3) { HalconCpp::HImage himage = HalconCpp::HImage(); std::vector imgchannel; split(image, imgchannel); cv::Mat imgB = imgchannel[0]; cv::Mat imgG = imgchannel[1]; cv::Mat imgR = imgchannel[2]; uchar* dataR = new uchar[hgt * wid]; uchar* dataG = new uchar[hgt * wid]; uchar* dataB = new uchar[hgt * wid]; for (i = 0; i < hgt; i++) { memcpy(dataR + wid * i, imgR.data + imgR.step * i, wid); memcpy(dataG + wid * i, imgG.data + imgG.step * i, wid); memcpy(dataB + wid * i, imgB.data + imgB.step * i, wid); } himage.GenImage3("byte", wid, hgt, dataR, dataG, dataB); HalconCpp::HString tmpType; Hlong tmpW, tmpH; void* hImgPtR, * hImgPtG, * hImgPtB; himage.GetImagePointer3(&hImgPtR, &hImgPtG, &hImgPtB, &tmpType, &tmpW, &tmpH); GenImage3(Hobj, "byte", wid, hgt, (Hlong)hImgPtR, (Hlong)hImgPtG, (Hlong)hImgPtB); delete[]dataR; delete[]dataG; delete[]dataB; dataR = NULL; dataG = NULL; dataB = NULL; } else if (image.type() == CV_8UC1) { //HalconCpp::HImage himage = HalconCpp::HImage(); ////uchar* data = new uchar[hgt * wid]; ////for (i = 0; i < hgt; i++) ////{ //// memcpy(data + wid * i, image.data + image.step * i, wid); ////} ////gen_image_const //himage.GenImage1("byte", wid, hgt, image.data); //HalconCpp::HString tmpType; //Hlong tmpW, tmpH; //void* hImgPt; //hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); //GenImage1(&Hobj, "byte", wid, hgt, (Hlong)hImgPt); GenImage1(Hobj, "byte", wid, hgt, (Hlong)image.data); /* delete[] data; data = NULL;*/ } else if (image.type() == CV_16UC1) { HalconCpp::HImage himage = HalconCpp::HImage(); ushort* data = new ushort[hgt * wid * 2]; memcpy(data, image.data, hgt * wid * 2); himage.GenImage1("uint2", wid, hgt, data); HalconCpp::HString tmpType; Hlong tmpW, tmpH; void* hImgPt; hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); GenImage1(Hobj, "uint2", wid, hgt, (Hlong)hImgPt); delete[] data; data = NULL; } else if (image.type() == CV_16SC1) { HalconCpp::HImage himage = HalconCpp::HImage(); short* data = new short[hgt * wid * 2]; memcpy(data, image.data, hgt * wid * 2); himage.GenImage1("int2", wid, hgt, data); HalconCpp::HString tmpType; Hlong tmpW, tmpH; void* hImgPt; hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); GenImage1(Hobj, "int2", wid, hgt, (Hlong)hImgPt); delete[] data; data = NULL; } } static HalconCpp::HObject MatToHObject(cv::Mat& image) { HalconCpp::HObject Hobj; int hgt = image.rows; int wid = image.cols; int i; if (image.type() == CV_8UC3) { HalconCpp::HImage himage = HalconCpp::HImage(); std::vector imgchannel; split(image, imgchannel); cv::Mat imgB = imgchannel[0]; cv::Mat imgG = imgchannel[1]; cv::Mat imgR = imgchannel[2]; uchar* dataR = new uchar[hgt * wid]; uchar* dataG = new uchar[hgt * wid]; uchar* dataB = new uchar[hgt * wid]; for (i = 0; i < hgt; i++) { memcpy(dataR + wid * i, imgR.data + imgR.step * i, wid); memcpy(dataG + wid * i, imgG.data + imgG.step * i, wid); memcpy(dataB + wid * i, imgB.data + imgB.step * i, wid); } himage.GenImage3("byte", wid, hgt, dataR, dataG, dataB); HalconCpp::HString tmpType; Hlong tmpW, tmpH; void* hImgPtR, * hImgPtG, * hImgPtB; himage.GetImagePointer3(&hImgPtR, &hImgPtG, &hImgPtB, &tmpType, &tmpW, &tmpH); GenImage3(&Hobj, "byte", wid, hgt, (Hlong)hImgPtR, (Hlong)hImgPtG, (Hlong)hImgPtB); delete[]dataR; delete[]dataG; delete[]dataB; dataR = NULL; dataG = NULL; dataB = NULL; } else if (image.type() == CV_8UC1) { //HalconCpp::HImage himage = HalconCpp::HImage(); ////uchar* data = new uchar[hgt * wid]; ////for (i = 0; i < hgt; i++) ////{ //// memcpy(data + wid * i, image.data + image.step * i, wid); ////} ////gen_image_const //himage.GenImage1("byte", wid, hgt, image.data); //HalconCpp::HString tmpType; //Hlong tmpW, tmpH; //void* hImgPt; //hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); //GenImage1(&Hobj, "byte", wid, hgt, (Hlong)hImgPt); GenImage1(&Hobj, "byte", wid, hgt, (Hlong)image.data); /* delete[] data; data = NULL;*/ } else if (image.type() == CV_16UC1) { HalconCpp::HImage himage = HalconCpp::HImage(); ushort* data = new ushort[hgt * wid * 2]; memcpy(data, image.data, hgt * wid * 2); himage.GenImage1("uint2", wid, hgt, data); HalconCpp::HString tmpType; Hlong tmpW, tmpH; void* hImgPt; hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); GenImage1(&Hobj, "uint2", wid, hgt, (Hlong)hImgPt); delete[] data; data = NULL; } else if (image.type() == CV_16SC1) { HalconCpp::HImage himage = HalconCpp::HImage(); short* data = new short[hgt * wid * 2]; memcpy(data, image.data, hgt * wid * 2); himage.GenImage1("int2", wid, hgt, data); HalconCpp::HString tmpType; Hlong tmpW, tmpH; void* hImgPt; hImgPt = himage.GetImagePointer1(&tmpType, &tmpW, &tmpH); GenImage1(&Hobj, "int2", wid, hgt, (Hlong)hImgPt); delete[] data; data = NULL; } return Hobj; } //获取两点之间的直线距离 static double getDistance(cv::Point pointO, cv::Point pointA) { double distance; distance = powf((pointO.x - pointA.x), 2) + powf((pointO.y - pointA.y), 2); distance = sqrtf(distance); return distance; } //获取点到线段之间的直线距离 static double pointToLineDistance(const cv::Point& point, const cv::Point& lineStart, const cv::Point& lineEnd) { double numerator = std::abs((lineEnd.y - lineStart.y) * point.x - (lineEnd.x - lineStart.x) * point.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x); double denominator = std::sqrt(std::pow(lineEnd.y - lineStart.y, 2) + std::pow(lineEnd.x - lineStart.x, 2)); return numerator / denominator; } //计算角度,pt2为顶点 static double calculateAngle(cv::Point pt1, cv::Point pt2, cv::Point pt3) { // 计算两个向量 cv::Point vector1 = pt1 - pt2; cv::Point vector2 = pt3 - pt2; // 计算两个向量的长度 double length1 = norm(vector1); double length2 = norm(vector2); // 计算两个向量的点积 double dotProduct = vector1.dot(vector2); // 计算夹角的余弦值 double cosAngle = dotProduct / (length1 * length2); // 计算夹角的弧度 double angleRadians = acos(cosAngle); // 将弧度转换为度数 double angleDegrees = angleRadians * 180 / CV_PI; return angleDegrees; } //计算角度偏移量,pt1,pt2为第一条线段,pt3,pt4为第二条线段 static double calculateAngleOffset(cv::Point pt11, cv::Point pt12, cv::Point pt21, cv::Point pt22) { // 计算第一条线段的向量 cv::Point vec1(pt12.x - pt11.x, pt12.y - pt11.y); // 计算第二条线段的向量 cv::Point vec2(pt22.x - pt21.x, pt22.y - pt21.y); // 计算向量的叉乘 double crossProduct = vec1.x * vec2.y - vec1.y * vec2.x; // 计算角度偏移量 double angleOffset = atan2(crossProduct, vec1.dot(vec2)); // 将弧度转换为度数 angleOffset = angleOffset * 180 / CV_PI; return angleOffset; } //通过角度计算圆上点的位置 static cv::Point calculatePointOnCircle(cv::Point center, double radius, double angleDegrees) { // 将角度转换为弧度 double angleRadians = angleDegrees * CV_PI / 180.0; // 计算点的坐标 int x = static_cast(center.x + radius * cos(angleRadians)); int y = static_cast(center.y + radius * sin(angleRadians)); return cv::Point(x, y); } /// /// 模板匹配,以MatchResult返回 /// /// 图像 /// 模板图像 /// static MatchResult templateMatching(const cv::Mat& originalImage, const cv::Mat& templateImage) { MatchResult result; // 创建结果图像 int resultCols = originalImage.cols - templateImage.cols + 1; int resultRows = originalImage.rows - templateImage.rows + 1; cv::Mat resultImg(resultRows, resultCols, CV_32FC1); // 进行模板匹配 cv::matchTemplate(originalImage, templateImage, resultImg, cv::TM_CCOEFF_NORMED); // 归一化结果图像 cv::normalize(resultImg, resultImg, 0, 1, cv::NORM_MINMAX, -1, cv::Mat()); // 寻找最佳匹配位置 double minVal, maxVal; cv::Point minLoc, maxLoc; cv::minMaxLoc(resultImg, &minVal, &maxVal, &minLoc, &maxLoc); // 设置矩形框和可信度 result.boundingRect = cv::Rect(maxLoc.x, maxLoc.y, templateImage.cols, templateImage.rows); result.confidence = maxVal; return result; } /// /// 模板匹配,返回Rect以及分数 /// /// 模板图像 /// 匹配图像 /// 分数 /// static cv::Rect doMatchTemplate(const cv::Mat& templateImg, const cv::Mat& srcImg, double* sorce) { cv::Mat resultImage; matchTemplate(srcImg, templateImg, resultImage, cv::TM_CCOEFF_NORMED); double minVal = 0, maxVal = 0; cv::Point minLoc(0, 0), maxLoc(0, 0); minMaxLoc(resultImage, &minVal, &maxVal, &minLoc, &maxLoc); *sorce = maxVal; cv::Rect rect(maxLoc.x, maxLoc.y, templateImg.cols, templateImg.rows); return rect; } //计算contour是否重叠 static bool isContoursOverlap(const std::vector& contour1, const std::vector& contour2, double thresholdPercent = 0) { cv::Rect rect1 = cv::boundingRect(contour1); cv::Rect rect2 = cv::boundingRect(contour2); cv::Size tempImgSize(getMax(rect1.x + rect1.width, rect2.x + rect2.width), getMax(rect1.y + rect1.height, rect2.y + rect2.height)); cv::Mat binaryImage = cv::Mat::zeros(tempImgSize, CV_8U); // 画出目标轮廓 drawContours(binaryImage, std::vector>{contour2}, 0, cv::Scalar(255), -1); double totalOverlap = 0.0; // 创建一个临时的二值图像 cv::Mat tempBinaryImage = cv::Mat::zeros(tempImgSize, CV_8U); // 画出当前轮廓 drawContours(tempBinaryImage, std::vector>{contour1}, 0, cv::Scalar(255), -1); // 计算重叠的像素数 cv::Mat intersection = binaryImage & tempBinaryImage; int overlapPixelCount = countNonZero(intersection); // 计算当前轮廓的重叠百分比 double overlapPercentage = static_cast(overlapPixelCount) / countNonZero(tempBinaryImage); return overlapPercentage * 100.0 > thresholdPercent; // 将结果转换为百分比 } //计算rect是否重叠 static bool isRectOverlap(const cv::Rect& rect1, const cv::Rect& rect2, double thresholdPercent = 0) { // 计算两个矩形的重叠区域 cv::Rect intersection = rect1 & rect2; if (thresholdPercent != 0)//如果有阈值 { int maxCnotourArea = getMax(rect1.area(), rect2.area()); if (intersection.area() / maxCnotourArea < thresholdPercent) return false; } // 如果重叠区域的面积大于0,则说明两个矩形重叠 return intersection.area() > 0; } //将两个contour的区域合并 static std::vector contourPlusContour(const std::vector& contour1, const std::vector& contour2) { std::vectorres; res.insert(res.end(), contour1.begin(), contour1.end()); res.insert(res.end(), contour2.begin(), contour2.end()); return res; } static cv::Rect BboxToRect(handdleAI::Bbox input) { return cv::Rect(input.x - input.w / 2, input.y - input.h / 2, input.w, input.h); } enum ngTypeEnum { OK, huaHeng, aoKeng, zangWu }; struct rectAndData { cv::Rect rect; double defectHeight; double defectWidth; int badType = 0; bool result; ngTypeEnum ngType; std::vector contour; rectAndData(cv::Rect rectInput, double defectHeightInput, double defectWidthInput, bool resultInput) { rect = rectInput; defectHeight = defectHeightInput; defectWidth = defectWidthInput; result = resultInput; } rectAndData(cv::Rect rectInput, double defectHeightInput, double defectWidthInput, int n, bool resultInput) { rect = rectInput; defectHeight = defectHeightInput; defectWidth = defectWidthInput; badType = n; result = resultInput; } rectAndData(cv::Rect rectInput, double defectHeightInput, double defectWidthInput, int n, bool resultInput, std::vector& contourInput) { rect = rectInput; defectHeight = defectHeightInput; defectWidth = defectWidthInput; badType = n; result = resultInput; contour = contourInput; } rectAndData(cv::Rect rectInput, double defectHeightInput, double defectWidthInput, int n, bool resultInput, std::vector& contourInput, ngTypeEnum inputNgType) { rect = rectInput; defectHeight = defectHeightInput; defectWidth = defectWidthInput; badType = n; result = resultInput; contour = contourInput; ngType = inputNgType; } }; ; //平铺图像 static cv::Mat tiledImages(std::vector& imgs) { if (imgs.size() == 0) return cv::imread("./noimg.png",-1); if (imgs.size() == 1) return imgs[0]; for (auto& img : imgs) { if (img.channels() == 1) cv::cvtColor(img, img, cv::COLOR_GRAY2BGR); } //总图像大小 平铺显示 int matWidth = 0, matHeight = 0; for (auto& mat : imgs) { matHeight = getMax(mat.rows, matHeight); matWidth += mat.cols; } cv::Mat showMat(matHeight, matWidth, CV_8UC3); //平铺图像 int withCnt = 0; for (auto& resultMat : imgs) { resultMat.copyTo(showMat(cv::Rect(withCnt, 0, resultMat.cols, resultMat.rows))); withCnt += resultMat.cols; } return showMat; };