C++opencv中std::vector<Mat>和split

在C++中使用OpenCV库时,

std::vector<Mat> mv;

声明创建了一个std::vector容器,该容器用于存储cv::Mat对象。cv::Mat是OpenCV中用于表示图像的核心数据结构,可以存储实际的图像数据、图像的尺寸、类型等信息。使用std::vector<Mat>允许你处理和存储多个图像,这在很多图像处理任务中非常有用,如处理视频帧、存储图像处理的中间结果等。

使用std::vector的场景

视频处理:在处理视频时,你可能需要将视频帧存储在一个列表中进行批量处理。
图像通道分离:将彩色图像的不同颜色通道(如RGB通道)分离开来,每个通道作为一个单独的Mat对象存储。
批量图像处理:对一系列图像执行相同的操作,如图像滤波、边缘检测等。
特征提取:在进行特征提取时,可能需要存储多个图像的特征描述符。

示例:使用std::vector存储图像通道

以下是如何使用std::vector来分离和存储图像的RGB通道的简单示例:

#
#include <opencv2/opencv.hpp> #include <iostream> #include <fstream> #include <vector>   // 生成PLY点云文件(二进制格式,兼容CloudCompare) void savePointCloudToPLY(const cv::Mat & points, const cv::Mat & colors, const std::string & filename, double min_depth = 0.1, double max_depth = 10000.0) { std::ofstream plyFile(filename, std::ios::binary); if (!plyFile.is_open()) { std::cerr << "Error: Could not open PLY file for writing." << std::endl; return; }   // 统计有效点数量 int validCount = 0; for (int y = 0; y < points.rows; y++) { for (int x = 0; x < points.cols; x++) { cv::Vec3f point = points.at<cv::Vec3f>(y, x); // 检查深度值是否在有效范围内且不是无穷大 if (point[2] > min_depth && point[2] < max_depth && !std::isinf(point[0]) && !std::isinf(point[1]) && !std::isinf(point[2])) { validCount++; } } }   // 写入PLY头(二进制格式) plyFile << "ply\n"; plyFile << "format binary_little_endian 1.0\n"; plyFile << "element vertex " << validCount << "\n"; plyFile << "property float x\n"; plyFile << "property float y\n"; plyFile << "property float z\n"; plyFile << "property uchar red\n"; plyFile << "property uchar green\n"; plyFile << "property uchar blue\n"; plyFile << "end_header\n";   // 写入点云数据(二进制) for (int y = 0; y < points.rows; y++) { for (int x = 0; x < points.cols; x++) { cv::Vec3f point = points.at<cv::Vec3f>(y, x); cv::Vec3b color = colors.at<cv::Vec3b>(y, x);   // 过滤无效点 if (point[2] > min_depth && point[2] < max_depth && !std::isinf(point[0]) && !std::isinf(point[1]) && !std::isinf(point[2])) { // 写入坐标(小端序浮点数) plyFile.write(reinterpret_cast<const char*>(&point[0]), sizeof(float)); plyFile.write(reinterpret_cast<const char*>(&point[1]), sizeof(float)); plyFile.write(reinterpret_cast<const char*>(&point[2]), sizeof(float));   // 写入颜色(注意OpenCV使用BGR顺序) plyFile.write(reinterpret_cast<const char*>(&color[2]), sizeof(uint8_t)); // R plyFile.write(reinterpret_cast<const char*>(&color[1]), sizeof(uint8_t)); // G plyFile.write(reinterpret_cast<const char*>(&color[0]), sizeof(uint8_t)); // B } } }   plyFile.close(); std::cout << "Saved " << validCount << " points to " << filename << std::endl; }   int main() { // 1. 定义投影矩阵(使用您提供的数据) cv::Mat P1 = (cv::Mat_<double>(3, 4) << 2791.1531638651541, -3164.2425130586262, -1932.4112841478736, 9203066124.8177795410156, -3171.4539351046769, -2823.8486979863924, -2106.1412396703704, 11236470204.1201114654541, -0.0094942232932, -0.0067457239026, -0.9999321751664, 28815.1627210603910);   cv::Mat P2 = (cv::Mat_<double>(3, 4) << 2791.1885344521997, -3164.2173819650006, -1928.6860517671789, 9202236641.5202198028564, -3171.4539351046769, -2823.8486979863924, -2106.1412396703704, 11236470204.1201095581055, -0.0094942232932, -0.0067457239026, -0.9999321751664, 28815.1627210603947);   // 2. 读取视差图左影像(用于点云着色) cv::Mat disparity = cv::imread("C:\\Users\\lzr15\\Desktop\\data1\\data1\\DispImg\\017ER030_017ER031_DISP_ET0.tif", cv::IMREAD_UNCHANGED); // 16位视差图 cv::Mat leftImage = cv::imread("C:\\Users\\lzr15\\Desktop\\data1\\data1\\EpiImg\\017ER030_017ER031_ET0\\017ER030_REC.tif"); // 左核线影像   if (disparity.empty() || leftImage.empty()) { std::cerr << "Error: Could not read input images." << std::endl; return -1; }   // 3. 验证视差图数据 std::cout << "Disparity image size: " << disparity.size() << std::endl; std::cout << "Disparity image type: " << disparity.type() << " (CV_16UC1=" << CV_16UC1 << ")" << std::endl;   // 检查视差图是否为16位格式 if (disparity.type() != CV_16UC1 && disparity.type() != CV_16SC1) { std::cerr << "Warning: Disparity image is not 16-bit format. Results may be incorrect." << std::endl; }   // 4. 计算重投影矩阵Q cv::Mat K1, R1, t1, K2, R2, t2; cv::decomposeProjectionMatrix(P1, K1, R1, t1); cv::decomposeProjectionMatrix(P2, K2, R2, t2);   // 转换为非齐次坐标 t1 = t1.rowRange(0, 3) / t1.at<double>(3); t2 = t2.rowRange(0, 3) / t2.at<double>(3);   // 计算基线长度(单位:世界坐标单位) cv::Mat C1 = -R1.inv() * t1; cv::Mat C2 = -R2.inv() * t2; double Tx = cv::norm(C2 - C1);   // 提取内参参数 double fx = K1.at<double>(0, 0); double fy = K1.at<double>(1, 1); double cx = K1.at<double>(0, 2); double cy = K1.at<double>(1, 2);   std::cout << "\nCamera Parameters:" << std::endl; std::cout << " Focal length (fx, fy): " << fx << ", " << fy << std::endl; std::cout << " Principal point (cx, cy): " << cx << ", " << cy << std::endl; std::cout << " Baseline (Tx): " << Tx << " world units" << std::endl;   // 修改Q矩阵构造 double cx_right = K2.at<double>(0, 2); // 右相机主点x cv::Mat Q = (cv::Mat_<double>(4, 4) << 1.0, 0.0, 0.0, -cx, 0.0, 1.0, 0.0, -cy, 0.0, 0.0, 0.0, fx, 0.0, 0.0, -1.0 / Tx, (cx - cx_right) / Tx); // 关键修改   std::cout << "\nReprojection Matrix Q:" << std::endl; std::cout << Q << std::endl;   // 6. 处理视差图 - 关键步骤! cv::Mat disp_float;   // 修改视差图处理部分 double scale_factor = 1.0; if (disparity.type() == CV_16UC1) { scale_factor = 1.0 / 16.0; } else if (disparity.type() == CV_16SC1) { scale_factor = 1.0 / 16.0; } else if (disparity.type() == CV_32FC1) { // 显式匹配32FC1 scale_factor = 1.0; } else if (disparity.type() == CV_32F) { // 后备匹配通用浮点 scale_factor = 1.0; } else { std::cerr << "Warning: Unknown disparity format. Using default scaling." << std::endl; scale_factor = 1.0 / 16.0; } disparity.convertTo(disp_float, CV_32F, scale_factor);   // 7. 计算3D点云 cv::Mat xyz; cv::reprojectImageTo3D(disp_float, xyz, Q, false, CV_32F);   // 8. 提取深度图(Z通道) std::vector<cv::Mat> channels; cv::split(xyz, channels); cv::Mat depth = channels[2];   // 修改深度图可视化部分 cv::Mat depthVis; double minVal, maxVal;   // 创建掩码:有效点 = (深度>0) 且 (非无穷大) cv::Mat validMask = (depth > 0) & (depth < std::numeric_limits<float>::infinity()); cv::Mat validMask8U; validMask.convertTo(validMask8U, CV_8U);   // 仅对有效点计算深度范围 cv::minMaxLoc(depth, &minVal, &maxVal, nullptr, nullptr, validMask8U); std::cout << "Valid Depth Range: min = " << minVal << ", max = " << maxVal << std::endl;   if (maxVal > minVal) { // 归一化有效点到[0, 255] cv::Mat temp = cv::Mat::zeros(depth.size(), CV_32F); depth.copyTo(temp, validMask8U); // 仅复制有效点 temp.convertTo(depthVis, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal)); } else { depthVis = cv::Mat::zeros(depth.size(), CV_8U); }   cv::imwrite("depth_map.png", depthVis);   // 10. 生成彩色点云(使用二进制PLY格式) savePointCloudToPLY(xyz, leftImage, "point_cloud.ply");   std::cout << "\nProcessing completed." << std::endl; std::cout << "Depth map saved as 'depth_map.png'" << std::endl; std::cout << "Point cloud saved as 'point_cloud.ply'" << std::endl;   cv::waitKey(0); return 0; }这是一段生成深度图三位点云的代码,但是生成的三维点云质量很差,你帮我重新写一段C++代码,运行出高精度的三位点云
07-17
// 引入必要的头文件 #include <opencv2/opencv.hpp> // OpenCV 核心库,用于图像处理 #include <opencv2/features2d.hpp> // OpenCV 特征检测库 #include<opencv2/xfeatures2d/xfeatures2d.hpp> // OpenCV 额外特征检测库(SURF) #include <opencv2/stitching.hpp> // OpenCV 图像拼接库 #include <iostream> // 输入输出流,用于控制台输出 #include <vector> // 向量容器,用于存储多个图像 #include <string> // 字符串处理 #include <algorithm> // 算法库,用于排序等操作 #include <regex> // 正则表达式库,用于文件名解析 #include <sstream> // 字符串流,用于字符串处理 #include <iomanip> // 输入输出格式化 #include <chrono> // 时间库,用于性能测量 #include <thread> // 线程库,用于实时显示 #include <atomic> // 原子操作库 #include <mutex> // 互斥锁库 #include <opencv2/stitching/detail/util.hpp> // 可能包含 resultRoi 的定义 // 配置参数 - 这些是程序的设置,可以根据需要修改 const std::string IMAGE_DIR = "C:/Users/zhangqt203387/images"; // 图像文件夹路径 const std::string OUTPUT_DIR = "output"; // 输出文件夹名称 const int RESIZE_THRESHOLD = 1024; // 图像最大尺寸限制 const std::string FEATURE_DETECTOR = "SIFT"; // 使用的特征检测算法 const double RANSAC_THRESHOLD = 5.0; // RANSAC算法的容错阈值 const double MATCH_RATIO = 0.6; // 特征匹配的质量阈值 const int MIN_MATCHES = 10; // 最小匹配点数量 const bool SHOW_PROGRESS = true; // 是否显示拼接过程 const double DISPLAY_SCALE = 0.3; // 显示图像的缩放比例 // 图像预处理参数 const bool ENABLE_PREPROCESSING = true; // 是否启用图像预处理 const int MEDIAN_BLUR_SIZE = 3; // 中值滤波核大小 const double INDANE_CLIP_LIMIT = 2.0; // INDANE算法对比度限制 const int INDANE_TILE_SIZE = 8; // INDANE算法瓦片大小 // 特征提取参数 const int MAX_FEATURES = 5000; // 最大特征点数量 const double CONTRAST_THRESHOLD = 0.04; // 对比度阈值 // 图像融合参数 const bool USE_MULTI_BAND_BLENDING = true; // 是否使用多频带融合 const int BLEND_WIDTH = 5; // 融合宽度 // 定义图像拼接器类 class AdvancedImageStitcher { private: // 类的私有成员变量 std::string detectorType; // 特征检测器类型 cv::Ptr<cv::Feature2D> detector; // OpenCV特征检测器对象 cv::Ptr<cv::DescriptorMatcher> matcher; // 特征匹配器对象 std::vector<cv::Mat> images; // 存储加载的图像 std::vector<std::string> imagePaths; // 存储图像文件路径 cv::Mat panorama; // 存储拼接后的全景图 std::atomic<bool> stopDisplay; // 停止显示标志 std::mutex panoramaMutex; // 全景图互斥锁 // 设置特征检测器匹配器 void setupDetectors() { // 根据选择的类型创建特征检测器 if (detectorType == "SIFT") { detector = cv::SIFT::create(MAX_FEATURES, 3, CONTRAST_THRESHOLD); } else if (detectorType == "SURF") { detector = cv::xfeatures2d::SURF::create(400); // Hessian阈值 } else if (detectorType == "AKAZE") { detector = cv::AKAZE::create(); } else { // 默认使用ORB detector = cv::ORB::create(MAX_FEATURES); } // 根据特征检测器类型创建匹配器 if (detectorType == "SIFT" || detectorType == "SURF") { matcher = cv::FlannBasedMatcher::create(); // SIFTSURF使用FLANN匹配器 } else { matcher = cv::BFMatcher::create(cv::NORM_HAMMING, false); // 其他使用暴力匹配器 } } // INDANE图像增强算法 cv::Mat applyINDANE(const cv::Mat& input) { cv::Mat lab; cv::cvtColor(input, lab, cv::COLOR_BGR2Lab); std::vector<cv::Mat> labChannels; cv::split(lab, labChannels); // 对L通道应用CLAHE cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(); clahe->setClipLimit(INDANE_CLIP_LIMIT); clahe->setTilesGridSize(cv::Size(INDANE_TILE_SIZE, INDANE_TILE_SIZE)); clahe->apply(labChannels[0], labChannels[0]); cv::Mat enhancedLab; cv::merge(labChannels, enhancedLab); cv::Mat result; cv::cvtColor(enhancedLab, result, cv::COLOR_Lab2BGR); return result; } // 逆滤波去模糊 cv::Mat applyInverseFilter(const cv::Mat& input) { // 创建模糊核(假设轻微高斯模糊) cv::Mat kernel = cv::getGaussianKernel(15, 1.5); kernel = kernel * kernel.t(); // 将图像转换为浮点型 cv::Mat floatInput; input.convertTo(floatInput, CV_32F); // 应用维纳滤波(逆滤波的稳定版本) cv::Mat result; cv::filter2D(floatInput, result, -1, kernel); // 将结果转换回8位 result.convertTo(result, CV_8U); return result; } // 图像预处理函数 cv::Mat preprocessImage(const cv::Mat& input) { if (!ENABLE_PREPROCESSING) { return input.clone(); } cv::Mat result = input.clone(); // 步骤1: INDANE图像增强 result = applyINDANE(result); // 步骤2: 逆滤波去模糊 result = applyInverseFilter(result); // 步骤3: 中值滤波去噪 cv::medianBlur(result, result, MEDIAN_BLUR_SIZE); return result; } // 自然排序比较函数 - 使文件名中的数字按数值大小排序 static bool naturalCompare(const std::string& a, const std::string& b) { // 将字符串转换为小写,实现不区分大小写的比较 std::string aLower = a; std::string bLower = b; std::transform(aLower.begin(), aLower.end(), aLower.begin(), ::tolower); std::transform(bLower.begin(), bLower.end(), bLower.begin(), ::tolower); // 使用正则表达式分割字符串中的数字非数字部分 std::regex re("(\\d+)|(\\D+)"); std::sregex_iterator it1(aLower.begin(), aLower.end(), re); std::sregex_iterator it2(bLower.begin(), bLower.end(), re); std::sregex_iterator end; // 逐个比较分割后的部分 while (it1 != end && it2 != end) { std::smatch match1 = *it1; std::smatch match2 = *it2; // 如果两部分都是数字,则按数值比较 if (std::regex_match(match1.str(), std::regex("\\d+")) && std::regex_match(match2.str(), std::regex("\\d+"))) { int num1 = std::stoi(match1.str()); // 将字符串转换为整数 int num2 = std::stoi(match2.str()); if (num1 != num2) return num1 < num2; // 数值比较 } // 否则按字符串比较 else if (match1.str() != match2.str()) { return match1.str() < match2.str(); // 字符串比较 } // 移动到下一个部分 ++it1; ++it2; } // 如果前面的部分都相同,则按长度比较 return aLower.length() < bLower.length(); } // 特征匹配函数 std::vector<cv::DMatch> matchFeatures(const cv::Mat& des1, const cv::Mat& des2) { // 使用KNN匹配(K近邻匹配)找到最佳匹配 std::vector<std::vector<cv::DMatch>> knnMatches; matcher->knnMatch(des1, des2, knnMatches, 2); // k=2表示每个点找两个最佳匹配 // 筛选优质匹配点 std::vector<cv::DMatch> goodMatches; for (const auto& matchPair : knnMatches) { if (matchPair.size() == 2) { // 应用比率测试:最佳匹配的距离应小于次佳匹配距离的一定比例 if (matchPair[0].distance < MATCH_RATIO * matchPair[1].distance) { goodMatches.push_back(matchPair[0]); // 保留优质匹配 } } } // 输出匹配点数量信息 std::cout << "良好匹配点: " << goodMatches.size() << std::endl; return goodMatches; } // 计算单应性矩阵函数 cv::Mat findHomography(const std::vector<cv::KeyPoint>& kp1, const std::vector<cv::KeyPoint>& kp2, const std::vector<cv::DMatch>& matches, int& inliers) { // 检查是否有足够的匹配点 if (matches.size() < MIN_MATCHES) { return cv::Mat(); // 返回空矩阵 } // 提取匹配点的坐标 std::vector<cv::Point2f> srcPoints, dstPoints; for (const auto& match : matches) { srcPoints.push_back(kp2[match.trainIdx].pt); // 第二幅图像中的点 dstPoints.push_back(kp1[match.queryIdx].pt); // 第一幅图像中的点 } // 使用RANSAC算法计算单应性矩阵 cv::Mat mask; // 掩码,标记哪些点是内点(正确匹配) cv::Mat H = cv::findHomography(srcPoints, dstPoints, cv::RANSAC, RANSAC_THRESHOLD, mask); // 计算内点数量(正确匹配的点) inliers = 0; if (!mask.empty()) { inliers = cv::countNonZero(mask); // 统计非零元素数量 } // 输出内点信息 std::cout << "内点数量: " << inliers << "/" << matches.size() << std::endl; return H; // 返回单应性矩阵 } // 多频带融合 cv::Mat multiBandBlend(const std::vector<cv::Mat>& images, const std::vector<cv::Mat>& transforms) { if (images.empty()) return cv::Mat(); // 创建多频带混合器 cv::Ptr<cv::detail::MultiBandBlender> blender = cv::makePtr< cv::detail::MultiBandBlender >(); blender->setNumBands(static_cast<int>(ceil(log2(BLEND_WIDTH)) - 1)); // 准备混合器 cv::Rect dst_roi = cv::detail::resultRoi(transforms, images); blender->prepare(dst_roi); // 混合所有图像 for (size_t i = 0; i < images.size(); i++) { cv::Mat warped; cv::warpPerspective(images[i], warped, transforms[i], dst_roi.size()); // 创建掩码 cv::Mat mask = cv::Mat::ones(images[i].size(), CV_8U) * 255; cv::Mat warped_mask; cv::warpPerspective(mask, warped_mask, transforms[i], dst_roi.size()); // 混合图像 blender->feed(warped, warped_mask, cv::Point(0, 0)); } // 完成混合 cv::Mat result, result_mask; blender->blend(result, result_mask); return result; } // 加权平均融合 cv::Mat weightedBlend(const cv::Mat& img1, const cv::Mat& img2, const cv::Mat& H) { // 计算变换后图像的大小 std::vector<cv::Point2f> corners = { cv::Point2f(0, 0), cv::Point2f(img2.cols, 0), cv::Point2f(img2.cols, img2.rows), cv::Point2f(0, img2.rows) }; std::vector<cv::Point2f> warpedCorners; cv::perspectiveTransform(corners, warpedCorners, H); // 计算边界 float x_min = 0, x_max = img1.cols; float y_min = 0, y_max = img1.rows; for (const auto& pt : warpedCorners) { x_min = std::min(x_min, pt.x); x_max = std::max(x_max, pt.x); y_min = std::min(y_min, pt.y); y_max = std::max(y_max, pt.y); } // 创建结果图像 cv::Mat result(cv::Size(std::ceil(x_max - x_min), std::ceil(y_max - y_min)), img1.type()); // 应用变换 cv::Mat translation = cv::Mat::eye(3, 3, CV_64F); translation.at<double>(0, 2) = -x_min; translation.at<double>(1, 2) = -y_min; cv::Mat H_adjusted = translation * H; cv::warpPerspective(img2, result, H_adjusted, result.size()); // 叠加第一幅图像(使用加权平均) for (int y = 0; y < img1.rows; y++) { for (int x = 0; x < img1.cols; x++) { int result_x = x - x_min; int result_y = y - y_min; if (result_x >= 0 && result_x < result.cols && result_y >= 0 && result_y < result.rows) { cv::Vec3b pixel1 = img1.at<cv::Vec3b>(y, x); cv::Vec3b pixel2 = result.at<cv::Vec3b>(result_y, result_x); // 计算权重(简单线性混合) double weight = 0.5; if (x < img1.cols * 0.1) weight = 0.9; // 边缘区域权重更高 else if (x > img1.cols * 0.9) weight = 0.1; cv::Vec3b blended; for (int c = 0; c < 3; c++) { blended[c] = cv::saturate_cast<uchar>(weight * pixel1[c] + (1 - weight) * pixel2[c]); } result.at<cv::Vec3b>(result_y, result_x) = blended; } } } return result; } // 显示图像函数(用于中间过程) void showImage(const std::string& title, const cv::Mat& image) { if (image.empty()) return; // 检查图像是否为空 // 计算缩放比例,确保图像适合屏幕显示 int origH = image.rows; int origW = image.cols; double scale = std::min(DISPLAY_SCALE, 800.0 / origW); scale = std::min(scale, 600.0 / origH); scale = std::max(scale, 0.1); // 确保最小缩放比例 // 缩放图像 cv::Mat displayImg; cv::resize(image, displayImg, cv::Size(), scale, scale); // 显示图像 cv::imshow(title, displayImg); cv::waitKey(1); // 非阻塞等待,允许图像更新 } // 实时显示线程函数 void displayThreadFunc() { while (!stopDisplay) { { std::lock_guard<std::mutex> lock(panoramaMutex); if (!panorama.empty()) { showImage("实时拼接进度", panorama); } } std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 每100ms更新一次 } cv::destroyWindow("实时拼接进度"); } public: // 构造函数:初始化特征检测器类型并设置检测器 AdvancedImageStitcher(const std::string& detectorType = "SIFT") : detectorType(detectorType), stopDisplay(false) { setupDetectors(); } // 析构函数 ~AdvancedImageStitcher() { stopDisplay = true; } // 加载图像函数 void loadImages(const std::string& imageDir) { // 支持的图像扩展名 std::vector<std::string> extensions = { ".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif" }; // 使用OpenCV的glob函数查找图像文件 std::vector<cv::String> filePaths; cv::glob(imageDir + "/*", filePaths); std::vector<std::string> imagePaths; // 筛选图像文件 for (const auto& path : filePaths) { std::string ext = path.substr(path.find_last_of(".")); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); if (std::find(extensions.begin(), extensions.end(), ext) != extensions.end()) { imagePaths.push_back(path); } } // 检查是否找到图像文件 if (imagePaths.empty()) { std::cerr << "在 '" << imageDir << "' 目录下未找到任何图像文件" << std::endl; return; } // 使用自然排序对图像路径进行排序 std::sort(imagePaths.begin(), imagePaths.end(), naturalCompare); // 输出找到的图像信息 std::cout << "找到 " << imagePaths.size() << " 张图像:" << std::endl; for (size_t i = 0; i < imagePaths.size(); i++) { std::string fileName = imagePaths[i].substr(imagePaths[i].find_last_of("\\/") + 1); std::cout << " " << i + 1 << ": " << fileName << std::endl; } // 保存图像路径 this->imagePaths = imagePaths; // 加载并预处理图像 for (const auto& path : imagePaths) { // 读取图像 cv::Mat img = cv::imread(path); if (img.empty()) { std::cout << "无法加载图像: " << path << std::endl; continue; } // 图像预处理 cv::Mat processedImg = preprocessImage(img); // 缩减图像分辨率(如果太大) int h = processedImg.rows; int w = processedImg.cols; if (w > RESIZE_THRESHOLD || h > RESIZE_THRESHOLD) { double scale = RESIZE_THRESHOLD / static_cast<double>(std::max(w, h)); int newW = static_cast<int>(w * scale); int newH = static_cast<int>(h * scale); cv::resize(processedImg, processedImg, cv::Size(newW, newH), 0, 0, cv::INTER_AREA); std::cout << "已缩放: " << w << "x" << h << " -> " << newW << "x" << newH << std::endl; } // 将图像添加到向量中 images.push_back(processedImg); } // 检查是否成功加载了图像 if (images.empty()) { std::cerr << "未能成功加载任何图像" << std::endl; } } // 拼接图像函数 cv::Mat stitch() { // 如果只有一张或没有图像,直接返回 if (images.size() <= 1) { panorama = images.empty() ? cv::Mat() : images[0].clone(); return panorama; } // 启动实时显示线程 std::thread displayThread; if (SHOW_PROGRESS) { displayThread = std::thread(&AdvancedImageStitcher::displayThreadFunc, this); } // 从第一张图像开始 panorama = images[0].clone(); // 存储所有变换矩阵 std::vector<cv::Mat> transforms; transforms.push_back(cv::Mat::eye(3, 3, CV_64F)); // 第一张图像的变换矩阵是单位矩阵 // 依次拼接后续图像 for (size_t i = 1; i < images.size(); i++) { std::cout << "\n拼接图像 " << i + 1 << "/" << images.size() << std::endl; // 转换为灰度图(特征检测需要灰度图像) cv::Mat panoramaGray, newImgGray; cv::cvtColor(panorama, panoramaGray, cv::COLOR_BGR2GRAY); cv::cvtColor(images[i], newImgGray, cv::COLOR_BGR2GRAY); // 提取特征点特征描述符 std::vector<cv::KeyPoint> kp1, kp2; cv::Mat des1, des2; detector->detectAndCompute(panoramaGray, cv::noArray(), kp1, des1); detector->detectAndCompute(newImgGray, cv::noArray(), kp2, des2); // 检查是否有足够的特征点 if (des1.empty() || des2.empty() || des1.rows < MIN_MATCHES || des2.rows < MIN_MATCHES) { std::cout << "特征点不足" << std::endl; continue; } // 特征匹配 std::vector<cv::DMatch> matches = matchFeatures(des1, des2); if (matches.size() < MIN_MATCHES) { std::cout << "匹配点不足: " << matches.size() << " < " << MIN_MATCHES << std::endl; continue; } // 计算单应性矩阵 int inliers; cv::Mat H = findHomography(kp1, kp2, matches, inliers); // 检查单应性矩阵是否有效 if (H.empty() || inliers < MIN_MATCHES) { std::cout << "单应性矩阵计算失败或内点不足: " << inliers << std::endl; continue; } // 保存变换矩阵 transforms.push_back(H); // 应用变换并融合图像 if (USE_MULTI_BAND_BLENDING && i == images.size() - 1) { // 如果是最后一张图像,使用多频带融合 std::cout << "使用多频带融合..." << std::endl; std::vector<cv::Mat> allImages(images.begin(), images.begin() + i + 1); cv::Mat newPanorama = multiBandBlend(allImages, transforms); if (!newPanorama.empty()) { std::lock_guard<std::mutex> lock(panoramaMutex); panorama = newPanorama; } } else { // 否则使用加权平均融合 cv::Mat newPanorama = weightedBlend(panorama, images[i], H); if (!newPanorama.empty()) { std::lock_guard<std::mutex> lock(panoramaMutex); panorama = newPanorama; } } // 如果启用进度显示,更新当前拼接状态 if (SHOW_PROGRESS) { std::lock_guard<std::mutex> lock(panoramaMutex); // 显示线程会自动处理显示 } } // 停止显示线程 if (SHOW_PROGRESS) { stopDisplay = true; displayThread.join(); } return panorama; // 返回最终的全景图 } // 显示最终结果 void showResult() { if (panorama.empty()) { std::cout << "没有可显示的结果" << std::endl; return; } // 计算缩放比例,确保图像适合屏幕显示 int h = panorama.rows; int w = panorama.cols; double scale = std::min(DISPLAY_SCALE, 1200.0 / w); scale = std::min(scale, 800.0 / h); scale = std::max(scale, 0.1); // 确保最小缩放比例 // 缩放图像 cv::Mat displayImg; cv::resize(panorama, displayImg, cv::Size(), scale, scale); // 创建窗口并显示结果 cv::namedWindow("拼接结果", cv::WINDOW_NORMAL); cv::imshow("拼接结果", displayImg); // 等待用户按键 std::cout << "按任意键关闭窗口..." << std::endl; cv::waitKey(0); cv::destroyAllWindows(); // 关闭所有OpenCV窗口 } // 保存结果到文件 bool saveResult(const std::string& filename = "panorama.png") { if (panorama.empty()) { std::cout << "没有可保存的结果" << std::endl; return false; } // 保存图像 std::string path = OUTPUT_DIR + "/" + filename; bool success = cv::imwrite(path, panorama); if (success) { std::cout << "结果已保存到: " << path << std::endl; } else { std::cout << "保存失败: " << path << std::endl; } return success; } }; // 主函数 int main() { // 创建拼接器对象,使用配置的特征检测器类型 AdvancedImageStitcher stitcher(FEATURE_DETECTOR); try { // 加载图像 std::cout << "加载图像..." << std::endl; stitcher.loadImages(IMAGE_DIR); // 拼接图像 std::cout << "开始拼接..." << std::endl; auto start_time = std::chrono::high_resolution_clock::now(); cv::Mat result = stitcher.stitch(); auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time); std::cout << "拼接完成,耗时: " << duration.count() << "ms" << std::endl; // 如果有结果,显示并保存 if (!result.empty()) { // 显示结果 std::cout << "显示结果..." << std::endl; stitcher.showResult(); // 保存结果 std::string filename = "industrial_panorama_" + FEATURE_DETECTOR + ".png"; std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower); stitcher.saveResult(filename); } else { std::cout << "拼接失败" << std::endl; } } catch (const std::exception& e) { // 捕获并输出异常 std::cerr << "错误: " << e.what() << std::endl; } return 0; // 程序正常退出 }代码改正
最新发布
08-27
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法小菜鸟moon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值